import type { AuthSession } from './auth';
import { getAuthSession } from './auth';
import { getNocoDbConfiguration } from './nocodb';

export interface AuditLogEntry {
  id?: number;
  timestamp: string;
  user_id: string;
  user_email: string;
  user_groups: string[];
  action: string;
  resource_type: string;
  resource_id?: string;
  changes?: any;
  metadata: {
    ip_address?: string;
    user_agent?: string;
    session_id?: string;
    request_url?: string;
    request_method?: string;
    response_status?: number;
    request_duration?: number;
  };
  status: 'success' | 'failure';
  error_message?: string;
}

export interface AuditLogFilters {
  startDate?: string;
  endDate?: string;
  userId?: string;
  userEmail?: string;
  action?: string;
  resourceType?: string;
  status?: 'success' | 'failure';
  limit?: number;
  offset?: number;
}

// NocoDB table ID for audit logs (will be updated when table is created)
const AUDIT_LOGS_TABLE_ID = "audit_logs"; // Placeholder - update with actual table ID

/**
 * Log an audit event
 */
export const logAuditEvent = async (
  event: any,
  action: string,
  resourceType: string,
  details: {
    resourceId?: string;
    changes?: any;
    status?: 'success' | 'failure';
    errorMessage?: string;
    duration?: number;
  } = {}
): Promise<void> => {
  try {
    // Get user info from the event
    let userInfo = {
      user_id: 'anonymous',
      user_email: 'anonymous',
      user_groups: [] as string[]
    };

    try {
      const authSession = await getAuthSession(event);
      if (authSession.authenticated && authSession.user) {
        userInfo = {
          user_id: authSession.user.id,
          user_email: authSession.user.email,
          user_groups: authSession.groups || []
        };
      }
    } catch (authError) {
      // Continue with anonymous user if auth fails
      console.warn('[AUDIT] Could not get user info:', authError);
    }

    // Extract request metadata
    const headers = event.node.req.headers;
    const metadata = {
      ip_address: getClientIP(event),
      user_agent: headers['user-agent'] || 'unknown',
      session_id: headers['cookie']?.split('nuxt-oidc-auth=')[1]?.split(';')[0] || 'none',
      request_url: event.node.req.url || 'unknown',
      request_method: event.node.req.method || 'unknown',
      response_status: details.status === 'failure' ? 500 : 200,
      request_duration: details.duration || 0
    };

    // Create audit log entry
    const auditEntry: Omit<AuditLogEntry, 'id'> = {
      timestamp: new Date().toISOString(),
      user_id: userInfo.user_id,
      user_email: userInfo.user_email,
      user_groups: userInfo.user_groups,
      action,
      resource_type: resourceType,
      resource_id: details.resourceId,
      changes: details.changes,
      metadata,
      status: details.status || 'success',
      error_message: details.errorMessage
    };

    // Store in NocoDB
    await storeAuditLog(auditEntry);

    console.log('[AUDIT] Logged event:', {
      action,
      resourceType,
      resourceId: details.resourceId,
      user: userInfo.user_email,
      status: details.status || 'success'
    });

  } catch (error) {
    console.error('[AUDIT] Failed to log audit event:', error);
    // Don't throw - audit logging should not break the main request
  }
};

/**
 * Store audit log in NocoDB
 */
const storeAuditLog = async (entry: Omit<AuditLogEntry, 'id'>): Promise<void> => {
  const config = getNocoDbConfiguration();
  
  // Prepare data for NocoDB
  const data = {
    timestamp: entry.timestamp,
    user_id: entry.user_id,
    user_email: entry.user_email,
    user_groups: JSON.stringify(entry.user_groups),
    action: entry.action,
    resource_type: entry.resource_type,
    resource_id: entry.resource_id || null,
    changes: entry.changes ? JSON.stringify(entry.changes) : null,
    metadata: JSON.stringify(entry.metadata),
    status: entry.status,
    error_message: entry.error_message || null
  };

  try {
    await $fetch(`${config.url}/api/v2/tables/${AUDIT_LOGS_TABLE_ID}/records`, {
      method: 'POST',
      headers: {
        'xc-token': config.token,
        'Content-Type': 'application/json'
      },
      body: data
    });
  } catch (error) {
    console.error('[AUDIT] Failed to store audit log in NocoDB:', error);
    throw error;
  }
};

/**
 * Get audit logs with filtering
 */
export const getAuditLogs = async (filters: AuditLogFilters = {}): Promise<{
  list: AuditLogEntry[];
  totalCount: number;
}> => {
  const config = getNocoDbConfiguration();
  
  try {
    const params: any = {
      limit: filters.limit || 100,
      offset: filters.offset || 0,
      sort: '-timestamp' // Newest first
    };

    // Build where clause
    const whereConditions: string[] = [];
    
    if (filters.startDate) {
      whereConditions.push(`(timestamp,gte,${filters.startDate})`);
    }
    
    if (filters.endDate) {
      whereConditions.push(`(timestamp,lte,${filters.endDate})`);
    }
    
    if (filters.userId) {
      whereConditions.push(`(user_id,eq,${filters.userId})`);
    }
    
    if (filters.userEmail) {
      whereConditions.push(`(user_email,like,%${filters.userEmail}%)`);
    }
    
    if (filters.action) {
      whereConditions.push(`(action,eq,${filters.action})`);
    }
    
    if (filters.resourceType) {
      whereConditions.push(`(resource_type,eq,${filters.resourceType})`);
    }
    
    if (filters.status) {
      whereConditions.push(`(status,eq,${filters.status})`);
    }

    if (whereConditions.length > 0) {
      params.where = whereConditions.join('~and');
    }

    const response = await $fetch(`${config.url}/api/v2/tables/${AUDIT_LOGS_TABLE_ID}/records`, {
      headers: {
        'xc-token': config.token
      },
      params
    }) as any;

    // Parse JSON fields
    const parsedList = (response.list || []).map((item: any) => ({
      ...item,
      user_groups: JSON.parse(item.user_groups || '[]'),
      changes: item.changes ? JSON.parse(item.changes) : null,
      metadata: JSON.parse(item.metadata || '{}')
    }));

    return {
      list: parsedList,
      totalCount: response.PageInfo?.totalRows || parsedList.length
    };

  } catch (error) {
    console.error('[AUDIT] Failed to get audit logs:', error);
    return { list: [], totalCount: 0 };
  }
};

/**
 * Get audit statistics
 */
export const getAuditStats = async (days: number = 30): Promise<{
  totalEvents: number;
  successEvents: number;
  failureEvents: number;
  topActions: Array<{ action: string; count: number }>;
  topUsers: Array<{ user_email: string; count: number }>;
  dailyActivity: Array<{ date: string; count: number }>;
}> => {
  const endDate = new Date().toISOString();
  const startDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();

  try {
    const logs = await getAuditLogs({
      startDate,
      endDate,
      limit: 10000 // Get a large sample for stats
    });

    const totalEvents = logs.totalCount;
    const successEvents = logs.list.filter(log => log.status === 'success').length;
    const failureEvents = logs.list.filter(log => log.status === 'failure').length;

    // Calculate top actions
    const actionCounts = logs.list.reduce((acc, log) => {
      acc[log.action] = (acc[log.action] || 0) + 1;
      return acc;
    }, {} as Record<string, number>);

    const topActions = Object.entries(actionCounts)
      .sort(([,a], [,b]) => b - a)
      .slice(0, 10)
      .map(([action, count]) => ({ action, count }));

    // Calculate top users
    const userCounts = logs.list.reduce((acc, log) => {
      if (log.user_email !== 'anonymous') {
        acc[log.user_email] = (acc[log.user_email] || 0) + 1;
      }
      return acc;
    }, {} as Record<string, number>);

    const topUsers = Object.entries(userCounts)
      .sort(([,a], [,b]) => b - a)
      .slice(0, 10)
      .map(([user_email, count]) => ({ user_email, count }));

    // Calculate daily activity
    const dailyCounts = logs.list.reduce((acc, log) => {
      const date = log.timestamp.split('T')[0];
      acc[date] = (acc[date] || 0) + 1;
      return acc;
    }, {} as Record<string, number>);

    const dailyActivity = Object.entries(dailyCounts)
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([date, count]) => ({ date, count }));

    return {
      totalEvents,
      successEvents,
      failureEvents,
      topActions,
      topUsers,
      dailyActivity
    };

  } catch (error) {
    console.error('[AUDIT] Failed to get audit stats:', error);
    return {
      totalEvents: 0,
      successEvents: 0,
      failureEvents: 0,
      topActions: [],
      topUsers: [],
      dailyActivity: []
    };
  }
};

/**
 * Get client IP address from request
 */
const getClientIP = (event: any): string => {
  const headers = event.node.req.headers;
  
  // Check various headers for the real IP
  const forwarded = headers['x-forwarded-for'];
  const realIp = headers['x-real-ip'];
  const clientIp = headers['x-client-ip'];
  
  if (forwarded) {
    // x-forwarded-for can be a comma-separated list
    return forwarded.toString().split(',')[0].trim();
  }
  
  if (realIp) {
    return realIp.toString();
  }
  
  if (clientIp) {
    return clientIp.toString();
  }
  
  // Fallback to socket remote address
  return event.node.req.socket?.remoteAddress || 'unknown';
};

/**
 * Convenience functions for common audit events
 */
export const auditInterestCreated = (event: any, interestId: string, interestData: any) => {
  return logAuditEvent(event, 'CREATE_INTEREST', 'interest', {
    resourceId: interestId,
    changes: { created: interestData },
    status: 'success'
  });
};

export const auditInterestUpdated = (event: any, interestId: string, changes: any) => {
  return logAuditEvent(event, 'UPDATE_INTEREST', 'interest', {
    resourceId: interestId,
    changes,
    status: 'success'
  });
};

export const auditInterestDeleted = (event: any, interestId: string) => {
  return logAuditEvent(event, 'DELETE_INTEREST', 'interest', {
    resourceId: interestId,
    status: 'success'
  });
};

export const auditInterestMerged = (event: any, masterInterestId: string, mergedInterestIds: string[], mergeDetails: any) => {
  return logAuditEvent(event, 'MERGE_INTERESTS', 'interest', {
    resourceId: masterInterestId,
    changes: {
      master_interest_id: masterInterestId,
      merged_interest_ids: mergedInterestIds,
      merge_details: mergeDetails
    },
    status: 'success'
  });
};

export const auditLogin = (event: any, userId: string, userEmail: string) => {
  return logAuditEvent(event, 'USER_LOGIN', 'authentication', {
    resourceId: userId,
    changes: { user_email: userEmail },
    status: 'success'
  });
};

export const auditLogout = (event: any, userId: string, userEmail: string) => {
  return logAuditEvent(event, 'USER_LOGOUT', 'authentication', {
    resourceId: userId,
    changes: { user_email: userEmail },
    status: 'success'
  });
};

export const auditAccessDenied = (event: any, resource: string, requiredRoles: string[]) => {
  return logAuditEvent(event, 'ACCESS_DENIED', 'authorization', {
    resourceId: resource,
    changes: { required_roles: requiredRoles },
    status: 'failure',
    errorMessage: 'Insufficient permissions'
  });
};
