/**
 * @ai-summary Admin user data access layer
 * @ai-context Provides parameterized SQL queries for admin user operations
 */

import { Pool } from 'pg';
import { AdminUser, AdminAuditLog } from '../domain/admin.types';
import { logger } from '../../../core/logging/logger';

export class AdminRepository {
  constructor(private pool: Pool) {}

  async getAdminByAuth0Sub(auth0Sub: string): Promise<AdminUser | null> {
    const query = `
      SELECT auth0_sub, email, role, created_at, created_by, revoked_at, updated_at
      FROM admin_users
      WHERE auth0_sub = $1
      LIMIT 1
    `;

    try {
      const result = await this.pool.query(query, [auth0Sub]);
      if (result.rows.length === 0) {
        return null;
      }
      return this.mapRowToAdminUser(result.rows[0]);
    } catch (error) {
      logger.error('Error fetching admin by auth0_sub', { error, auth0Sub });
      throw error;
    }
  }

  async getAdminByEmail(email: string): Promise<AdminUser | null> {
    const query = `
      SELECT auth0_sub, email, role, created_at, created_by, revoked_at, updated_at
      FROM admin_users
      WHERE LOWER(email) = LOWER($1)
      LIMIT 1
    `;

    try {
      const result = await this.pool.query(query, [email]);
      if (result.rows.length === 0) {
        return null;
      }
      return this.mapRowToAdminUser(result.rows[0]);
    } catch (error) {
      logger.error('Error fetching admin by email', { error, email });
      throw error;
    }
  }

  async getAllAdmins(): Promise<AdminUser[]> {
    const query = `
      SELECT auth0_sub, email, role, created_at, created_by, revoked_at, updated_at
      FROM admin_users
      ORDER BY created_at DESC
    `;

    try {
      const result = await this.pool.query(query);
      return result.rows.map(row => this.mapRowToAdminUser(row));
    } catch (error) {
      logger.error('Error fetching all admins', { error });
      throw error;
    }
  }

  async getActiveAdmins(): Promise<AdminUser[]> {
    const query = `
      SELECT auth0_sub, email, role, created_at, created_by, revoked_at, updated_at
      FROM admin_users
      WHERE revoked_at IS NULL
      ORDER BY created_at DESC
    `;

    try {
      const result = await this.pool.query(query);
      return result.rows.map(row => this.mapRowToAdminUser(row));
    } catch (error) {
      logger.error('Error fetching active admins', { error });
      throw error;
    }
  }

  async createAdmin(auth0Sub: string, email: string, role: string, createdBy: string): Promise<AdminUser> {
    const query = `
      INSERT INTO admin_users (auth0_sub, email, role, created_by)
      VALUES ($1, $2, $3, $4)
      RETURNING auth0_sub, email, role, created_at, created_by, revoked_at, updated_at
    `;

    try {
      const result = await this.pool.query(query, [auth0Sub, email, role, createdBy]);
      if (result.rows.length === 0) {
        throw new Error('Failed to create admin user');
      }
      return this.mapRowToAdminUser(result.rows[0]);
    } catch (error) {
      logger.error('Error creating admin', { error, auth0Sub, email });
      throw error;
    }
  }

  async revokeAdmin(auth0Sub: string): Promise<AdminUser> {
    const query = `
      UPDATE admin_users
      SET revoked_at = CURRENT_TIMESTAMP
      WHERE auth0_sub = $1
      RETURNING auth0_sub, email, role, created_at, created_by, revoked_at, updated_at
    `;

    try {
      const result = await this.pool.query(query, [auth0Sub]);
      if (result.rows.length === 0) {
        throw new Error('Admin user not found');
      }
      return this.mapRowToAdminUser(result.rows[0]);
    } catch (error) {
      logger.error('Error revoking admin', { error, auth0Sub });
      throw error;
    }
  }

  async reinstateAdmin(auth0Sub: string): Promise<AdminUser> {
    const query = `
      UPDATE admin_users
      SET revoked_at = NULL
      WHERE auth0_sub = $1
      RETURNING auth0_sub, email, role, created_at, created_by, revoked_at, updated_at
    `;

    try {
      const result = await this.pool.query(query, [auth0Sub]);
      if (result.rows.length === 0) {
        throw new Error('Admin user not found');
      }
      return this.mapRowToAdminUser(result.rows[0]);
    } catch (error) {
      logger.error('Error reinstating admin', { error, auth0Sub });
      throw error;
    }
  }

  async logAuditAction(
    actorAdminId: string,
    action: string,
    targetAdminId?: string,
    resourceType?: string,
    resourceId?: string,
    context?: Record<string, any>
  ): Promise<AdminAuditLog> {
    const query = `
      INSERT INTO admin_audit_logs (actor_admin_id, target_admin_id, action, resource_type, resource_id, context)
      VALUES ($1, $2, $3, $4, $5, $6)
      RETURNING id, actor_admin_id, target_admin_id, action, resource_type, resource_id, context, created_at
    `;

    try {
      const result = await this.pool.query(query, [
        actorAdminId,
        targetAdminId || null,
        action,
        resourceType || null,
        resourceId || null,
        context ? JSON.stringify(context) : null,
      ]);

      if (result.rows.length === 0) {
        throw new Error('Failed to create audit log');
      }

      return this.mapRowToAuditLog(result.rows[0]);
    } catch (error) {
      logger.error('Error logging audit action', { error, actorAdminId, action });
      throw error;
    }
  }

  async getAuditLogs(limit: number = 100, offset: number = 0): Promise<{ logs: AdminAuditLog[]; total: number }> {
    const countQuery = 'SELECT COUNT(*) as total FROM admin_audit_logs';
    const query = `
      SELECT id, actor_admin_id, target_admin_id, action, resource_type, resource_id, context, created_at
      FROM admin_audit_logs
      ORDER BY created_at DESC
      LIMIT $1 OFFSET $2
    `;

    try {
      const [countResult, dataResult] = await Promise.all([
        this.pool.query(countQuery),
        this.pool.query(query, [limit, offset]),
      ]);

      const total = parseInt(countResult.rows[0].total, 10);
      const logs = dataResult.rows.map(row => this.mapRowToAuditLog(row));

      return { logs, total };
    } catch (error) {
      logger.error('Error fetching audit logs', { error });
      throw error;
    }
  }

  async updateAuth0SubByEmail(email: string, auth0Sub: string): Promise<AdminUser> {
    const query = `
      UPDATE admin_users
      SET auth0_sub = $1,
          updated_at = CURRENT_TIMESTAMP
      WHERE LOWER(email) = LOWER($2)
      RETURNING auth0_sub, email, role, created_at, created_by, revoked_at, updated_at
    `;

    try {
      const result = await this.pool.query(query, [auth0Sub, email]);
      if (result.rows.length === 0) {
        throw new Error(`Admin user with email ${email} not found`);
      }
      return this.mapRowToAdminUser(result.rows[0]);
    } catch (error) {
      logger.error('Error updating admin auth0_sub by email', { error, email, auth0Sub });
      throw error;
    }
  }

  private mapRowToAdminUser(row: any): AdminUser {
    return {
      auth0Sub: row.auth0_sub,
      email: row.email,
      role: row.role,
      createdAt: new Date(row.created_at),
      createdBy: row.created_by,
      revokedAt: row.revoked_at ? new Date(row.revoked_at) : null,
      updatedAt: new Date(row.updated_at),
    };
  }

  private mapRowToAuditLog(row: any): AdminAuditLog {
    return {
      id: row.id,
      actorAdminId: row.actor_admin_id,
      targetAdminId: row.target_admin_id,
      action: row.action,
      resourceType: row.resource_type,
      resourceId: row.resource_id,
      // JSONB columns are automatically parsed by pg driver - no JSON.parse needed
      context: row.context || undefined,
      createdAt: new Date(row.created_at),
    };
  }
}
