/**
 * @ai-summary Fastify route handlers for admin management API
 * @ai-context HTTP request/response handling with admin authorization and audit logging
 */

import { FastifyRequest, FastifyReply } from 'fastify';
import { AdminService } from '../domain/admin.service';
import { AdminRepository } from '../data/admin.repository';
import { pool } from '../../../core/config/database';
import { logger } from '../../../core/logging/logger';
import {
  CreateAdminInput,
  AdminAuth0SubInput,
  AuditLogsQueryInput,
  BulkCreateAdminInput,
  BulkRevokeAdminInput,
  BulkReinstateAdminInput
} from './admin.validation';
import {
  createAdminSchema,
  adminAuth0SubSchema,
  auditLogsQuerySchema,
  bulkCreateAdminSchema,
  bulkRevokeAdminSchema,
  bulkReinstateAdminSchema
} from './admin.validation';
import {
  BulkCreateAdminResponse,
  BulkRevokeAdminResponse,
  BulkReinstateAdminResponse,
  AdminUser
} from '../domain/admin.types';

export class AdminController {
  private adminService: AdminService;

  constructor() {
    const repository = new AdminRepository(pool);
    this.adminService = new AdminService(repository);
  }

  /**
   * GET /api/admin/verify - Verify admin access (for frontend auth checks)
   */
  async verifyAccess(request: FastifyRequest, reply: FastifyReply) {
    try {
      const userId = request.userContext?.userId;
      const userEmail = this.resolveUserEmail(request);

      console.log('[DEBUG] Admin verify - userId:', userId);
      console.log('[DEBUG] Admin verify - userEmail:', userEmail);

      if (userEmail && request.userContext) {
        request.userContext.email = userEmail.toLowerCase();
      }

      if (!userId && !userEmail) {
        console.log('[DEBUG] Admin verify - No userId or userEmail, returning 401');
        return reply.code(401).send({
          error: 'Unauthorized',
          message: 'User context missing'
        });
      }

      let adminRecord = userId
        ? await this.adminService.getAdminByAuth0Sub(userId)
        : null;

      console.log('[DEBUG] Admin verify - adminRecord by auth0Sub:', adminRecord ? 'FOUND' : 'NOT FOUND');

      // Fallback: attempt to resolve admin by email for legacy records
      if (!adminRecord && userEmail) {
        const emailMatch = await this.adminService.getAdminByEmail(userEmail.toLowerCase());

        console.log('[DEBUG] Admin verify - emailMatch:', emailMatch ? 'FOUND' : 'NOT FOUND');
        if (emailMatch) {
          console.log('[DEBUG] Admin verify - emailMatch.auth0Sub:', emailMatch.auth0Sub);
          console.log('[DEBUG] Admin verify - emailMatch.revokedAt:', emailMatch.revokedAt);
        }

        if (emailMatch && !emailMatch.revokedAt) {
          // If the stored auth0Sub differs, link it to the authenticated user
          if (userId && emailMatch.auth0Sub !== userId) {
            console.log('[DEBUG] Admin verify - Calling linkAdminAuth0Sub to update auth0Sub');
            adminRecord = await this.adminService.linkAdminAuth0Sub(userEmail, userId);
            console.log('[DEBUG] Admin verify - adminRecord after link:', adminRecord ? 'SUCCESS' : 'FAILED');
          } else {
            console.log('[DEBUG] Admin verify - Using emailMatch as adminRecord');
            adminRecord = emailMatch;
          }
        }
      }

      if (adminRecord && !adminRecord.revokedAt) {
        if (request.userContext) {
          request.userContext.isAdmin = true;
          request.userContext.adminRecord = adminRecord;
        }

        console.log('[DEBUG] Admin verify - Returning isAdmin: true');
        // User is an active admin
        return reply.code(200).send({
          isAdmin: true,
          adminRecord: {
            auth0Sub: adminRecord.auth0Sub,
            email: adminRecord.email,
            role: adminRecord.role
          }
        });
      }

      if (request.userContext) {
        request.userContext.isAdmin = false;
        request.userContext.adminRecord = undefined;
      }

      console.log('[DEBUG] Admin verify - Returning isAdmin: false');
      // User is not an admin
      return reply.code(200).send({
        isAdmin: false,
        adminRecord: null
      });
    } catch (error) {
      console.log('[DEBUG] Admin verify - Error caught:', error instanceof Error ? error.message : 'Unknown error');
      logger.error('Error verifying admin access', {
        error: error instanceof Error ? error.message : 'Unknown error',
        userId: request.userContext?.userId?.substring(0, 8) + '...'
      });

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Admin verification failed'
      });
    }
  }

  /**
   * GET /api/admin/admins - List all admin users
   */
  async listAdmins(request: FastifyRequest, reply: FastifyReply) {
    try {
      const actorId = request.userContext?.userId;

      if (!actorId) {
        return reply.code(401).send({
          error: 'Unauthorized',
          message: 'User context missing'
        });
      }

      const admins = await this.adminService.getAllAdmins();

      // Log VIEW action
      await this.adminService.getAdminByAuth0Sub(actorId);
      // Note: Not logging VIEW as it would create excessive audit entries
      // VIEW logging can be enabled if needed for compliance

      return reply.code(200).send({
        total: admins.length,
        admins
      });
    } catch (error: any) {
      logger.error('Error listing admins', {
        error: error.message,
        actorId: request.userContext?.userId
      });
      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to list admins'
      });
    }
  }

  /**
   * POST /api/admin/admins - Create new admin user
   */
  async createAdmin(
    request: FastifyRequest<{ Body: CreateAdminInput }>,
    reply: FastifyReply
  ) {
    try {
      const actorId = request.userContext?.userId;

      if (!actorId) {
        return reply.code(401).send({
          error: 'Unauthorized',
          message: 'User context missing'
        });
      }

      // Validate request body
      const validation = createAdminSchema.safeParse(request.body);
      if (!validation.success) {
        return reply.code(400).send({
          error: 'Bad Request',
          message: 'Invalid request body',
          details: validation.error.errors
        });
      }

      const { email, role } = validation.data;

      // Generate auth0Sub for the new admin
      // In production, this should be the actual Auth0 user ID
      // For now, we'll use email-based identifier
      const auth0Sub = `auth0|${email.replace('@', '_at_')}`;

      const admin = await this.adminService.createAdmin(
        email,
        role,
        auth0Sub,
        actorId
      );

      return reply.code(201).send(admin);
    } catch (error: any) {
      logger.error('Error creating admin', {
        error: error.message,
        actorId: request.userContext?.userId
      });

      if (error.message.includes('already exists')) {
        return reply.code(400).send({
          error: 'Bad Request',
          message: error.message
        });
      }

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to create admin'
      });
    }
  }

  /**
   * PATCH /api/admin/admins/:auth0Sub/revoke - Revoke admin access
   */
  async revokeAdmin(
    request: FastifyRequest<{ Params: AdminAuth0SubInput }>,
    reply: FastifyReply
  ) {
    try {
      const actorId = request.userContext?.userId;

      if (!actorId) {
        return reply.code(401).send({
          error: 'Unauthorized',
          message: 'User context missing'
        });
      }

      // Validate params
      const validation = adminAuth0SubSchema.safeParse(request.params);
      if (!validation.success) {
        return reply.code(400).send({
          error: 'Bad Request',
          message: 'Invalid auth0Sub parameter',
          details: validation.error.errors
        });
      }

      const { auth0Sub } = validation.data;

      // Check if admin exists
      const targetAdmin = await this.adminService.getAdminByAuth0Sub(auth0Sub);
      if (!targetAdmin) {
        return reply.code(404).send({
          error: 'Not Found',
          message: 'Admin user not found'
        });
      }

      // Revoke the admin (service handles last admin check)
      const admin = await this.adminService.revokeAdmin(auth0Sub, actorId);

      return reply.code(200).send(admin);
    } catch (error: any) {
      logger.error('Error revoking admin', {
        error: error.message,
        actorId: request.userContext?.userId,
        targetAuth0Sub: request.params.auth0Sub
      });

      if (error.message.includes('Cannot revoke the last active admin')) {
        return reply.code(400).send({
          error: 'Bad Request',
          message: error.message
        });
      }

      if (error.message.includes('not found')) {
        return reply.code(404).send({
          error: 'Not Found',
          message: error.message
        });
      }

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to revoke admin'
      });
    }
  }

  /**
   * PATCH /api/admin/admins/:auth0Sub/reinstate - Restore revoked admin
   */
  async reinstateAdmin(
    request: FastifyRequest<{ Params: AdminAuth0SubInput }>,
    reply: FastifyReply
  ) {
    try {
      const actorId = request.userContext?.userId;

      if (!actorId) {
        return reply.code(401).send({
          error: 'Unauthorized',
          message: 'User context missing'
        });
      }

      // Validate params
      const validation = adminAuth0SubSchema.safeParse(request.params);
      if (!validation.success) {
        return reply.code(400).send({
          error: 'Bad Request',
          message: 'Invalid auth0Sub parameter',
          details: validation.error.errors
        });
      }

      const { auth0Sub } = validation.data;

      // Check if admin exists
      const targetAdmin = await this.adminService.getAdminByAuth0Sub(auth0Sub);
      if (!targetAdmin) {
        return reply.code(404).send({
          error: 'Not Found',
          message: 'Admin user not found'
        });
      }

      // Reinstate the admin
      const admin = await this.adminService.reinstateAdmin(auth0Sub, actorId);

      return reply.code(200).send(admin);
    } catch (error: any) {
      logger.error('Error reinstating admin', {
        error: error.message,
        actorId: request.userContext?.userId,
        targetAuth0Sub: request.params.auth0Sub
      });

      if (error.message.includes('not found')) {
        return reply.code(404).send({
          error: 'Not Found',
          message: error.message
        });
      }

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to reinstate admin'
      });
    }
  }

  /**
   * GET /api/admin/audit-logs - Fetch audit trail
   */
  async getAuditLogs(
    request: FastifyRequest<{ Querystring: AuditLogsQueryInput }>,
    reply: FastifyReply
  ) {
    try {
      const actorId = request.userContext?.userId;

      if (!actorId) {
        return reply.code(401).send({
          error: 'Unauthorized',
          message: 'User context missing'
        });
      }

      // Validate query params
      const validation = auditLogsQuerySchema.safeParse(request.query);
      if (!validation.success) {
        return reply.code(400).send({
          error: 'Bad Request',
          message: 'Invalid query parameters',
          details: validation.error.errors
        });
      }

      const { limit, offset } = validation.data;

      const result = await this.adminService.getAuditLogs(limit, offset);

      return reply.code(200).send(result);
    } catch (error: any) {
      logger.error('Error fetching audit logs', {
        error: error.message,
        actorId: request.userContext?.userId
      });
      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to fetch audit logs'
      });
    }
  }

  /**
   * POST /api/admin/admins/bulk - Create multiple admin users
   */
  async bulkCreateAdmins(
    request: FastifyRequest<{ Body: BulkCreateAdminInput }>,
    reply: FastifyReply
  ) {
    try {
      const actorId = request.userContext?.userId;

      if (!actorId) {
        return reply.code(401).send({
          error: 'Unauthorized',
          message: 'User context missing'
        });
      }

      // Validate request body
      const validation = bulkCreateAdminSchema.safeParse(request.body);
      if (!validation.success) {
        return reply.code(400).send({
          error: 'Bad Request',
          message: 'Invalid request body',
          details: validation.error.errors
        });
      }

      const { admins } = validation.data;

      const created: AdminUser[] = [];
      const failed: Array<{ email: string; error: string }> = [];

      // Process each admin creation sequentially to maintain data consistency
      for (const adminInput of admins) {
        try {
          const { email, role = 'admin' } = adminInput;

          // Generate auth0Sub for the new admin
          // In production, this should be the actual Auth0 user ID
          const auth0Sub = `auth0|${email.replace('@', '_at_')}`;

          const admin = await this.adminService.createAdmin(
            email,
            role,
            auth0Sub,
            actorId
          );

          created.push(admin);
        } catch (error: any) {
          logger.error('Error creating admin in bulk operation', {
            error: error.message,
            email: adminInput.email,
            actorId
          });

          failed.push({
            email: adminInput.email,
            error: error.message || 'Failed to create admin'
          });
        }
      }

      const response: BulkCreateAdminResponse = {
        created,
        failed
      };

      // Return 207 Multi-Status if there were any failures, 201 if all succeeded
      const statusCode = failed.length > 0 ? 207 : 201;

      return reply.code(statusCode).send(response);
    } catch (error: any) {
      logger.error('Error in bulk create admins', {
        error: error.message,
        actorId: request.userContext?.userId
      });

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to process bulk admin creation'
      });
    }
  }

  /**
   * PATCH /api/admin/admins/bulk-revoke - Revoke multiple admin users
   */
  async bulkRevokeAdmins(
    request: FastifyRequest<{ Body: BulkRevokeAdminInput }>,
    reply: FastifyReply
  ) {
    try {
      const actorId = request.userContext?.userId;

      if (!actorId) {
        return reply.code(401).send({
          error: 'Unauthorized',
          message: 'User context missing'
        });
      }

      // Validate request body
      const validation = bulkRevokeAdminSchema.safeParse(request.body);
      if (!validation.success) {
        return reply.code(400).send({
          error: 'Bad Request',
          message: 'Invalid request body',
          details: validation.error.errors
        });
      }

      const { auth0Subs } = validation.data;

      const revoked: AdminUser[] = [];
      const failed: Array<{ auth0Sub: string; error: string }> = [];

      // Process each revocation sequentially to maintain data consistency
      for (const auth0Sub of auth0Subs) {
        try {
          // Check if admin exists
          const targetAdmin = await this.adminService.getAdminByAuth0Sub(auth0Sub);
          if (!targetAdmin) {
            failed.push({
              auth0Sub,
              error: 'Admin user not found'
            });
            continue;
          }

          // Attempt to revoke the admin
          const admin = await this.adminService.revokeAdmin(auth0Sub, actorId);
          revoked.push(admin);
        } catch (error: any) {
          logger.error('Error revoking admin in bulk operation', {
            error: error.message,
            auth0Sub,
            actorId
          });

          // Special handling for "last admin" constraint
          failed.push({
            auth0Sub,
            error: error.message || 'Failed to revoke admin'
          });
        }
      }

      const response: BulkRevokeAdminResponse = {
        revoked,
        failed
      };

      // Return 207 Multi-Status if there were any failures, 200 if all succeeded
      const statusCode = failed.length > 0 ? 207 : 200;

      return reply.code(statusCode).send(response);
    } catch (error: any) {
      logger.error('Error in bulk revoke admins', {
        error: error.message,
        actorId: request.userContext?.userId
      });

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to process bulk admin revocation'
      });
    }
  }

  /**
   * PATCH /api/admin/admins/bulk-reinstate - Reinstate multiple revoked admin users
   */
  async bulkReinstateAdmins(
    request: FastifyRequest<{ Body: BulkReinstateAdminInput }>,
    reply: FastifyReply
  ) {
    try {
      const actorId = request.userContext?.userId;

      if (!actorId) {
        return reply.code(401).send({
          error: 'Unauthorized',
          message: 'User context missing'
        });
      }

      // Validate request body
      const validation = bulkReinstateAdminSchema.safeParse(request.body);
      if (!validation.success) {
        return reply.code(400).send({
          error: 'Bad Request',
          message: 'Invalid request body',
          details: validation.error.errors
        });
      }

      const { auth0Subs } = validation.data;

      const reinstated: AdminUser[] = [];
      const failed: Array<{ auth0Sub: string; error: string }> = [];

      // Process each reinstatement sequentially to maintain data consistency
      for (const auth0Sub of auth0Subs) {
        try {
          // Check if admin exists
          const targetAdmin = await this.adminService.getAdminByAuth0Sub(auth0Sub);
          if (!targetAdmin) {
            failed.push({
              auth0Sub,
              error: 'Admin user not found'
            });
            continue;
          }

          // Attempt to reinstate the admin
          const admin = await this.adminService.reinstateAdmin(auth0Sub, actorId);
          reinstated.push(admin);
        } catch (error: any) {
          logger.error('Error reinstating admin in bulk operation', {
            error: error.message,
            auth0Sub,
            actorId
          });

          failed.push({
            auth0Sub,
            error: error.message || 'Failed to reinstate admin'
          });
        }
      }

      const response: BulkReinstateAdminResponse = {
        reinstated,
        failed
      };

      // Return 207 Multi-Status if there were any failures, 200 if all succeeded
      const statusCode = failed.length > 0 ? 207 : 200;

      return reply.code(statusCode).send(response);
    } catch (error: any) {
      logger.error('Error in bulk reinstate admins', {
        error: error.message,
        actorId: request.userContext?.userId
      });

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to process bulk admin reinstatement'
      });
    }
  }

  private resolveUserEmail(request: FastifyRequest): string | undefined {
    console.log('[DEBUG] resolveUserEmail - request.userContext:', JSON.stringify(request.userContext, null, 2));
    console.log('[DEBUG] resolveUserEmail - request.user:', JSON.stringify((request as any).user, null, 2));

    const candidates: Array<string | undefined> = [
      request.userContext?.email,
      (request as any).user?.email,
      (request as any).user?.['https://motovaultpro.com/email'],
      (request as any).user?.['https://motovaultpro.com/user_email'],
      (request as any).user?.preferred_username,
    ];

    console.log('[DEBUG] resolveUserEmail - candidates:', candidates);

    for (const value of candidates) {
      if (typeof value === 'string' && value.includes('@')) {
        console.log('[DEBUG] resolveUserEmail - found email:', value);
        return value.trim();
      }
    }
    console.log('[DEBUG] resolveUserEmail - no email found');
    return undefined;
  }
}
