import { Injectable, UnauthorizedException, Logger } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { DepartmentsService } from '../departments/departments.service';
import { ApplicantsService } from '../applicants/applicants.service';
import { UsersService } from '../users/users.service';
import { HashUtil } from '../../common/utils';
import { UserRole } from '../../common/enums';
import { ERROR_CODES } from '../../common/constants';
import { JwtPayload } from '../../common/interfaces/request-context.interface';
import { LoginDto, DigiLockerLoginDto, EmailPasswordLoginDto } from './dto';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  private readonly logger = new Logger(AuthService.name);

  constructor(
    private readonly jwtService: JwtService,
    private readonly departmentsService: DepartmentsService,
    private readonly applicantsService: ApplicantsService,
    private readonly usersService: UsersService,
  ) {}

  /**
   * Validate department API key and return JWT token
   */
  async validateDepartmentApiKey(
    apiKey: string,
    departmentCode: string,
  ): Promise<{ accessToken: string; department: { id: string; code: string; name: string } }> {
    const department = await this.departmentsService.findByCode(departmentCode);

    if (!department) {
      throw new UnauthorizedException({
        code: ERROR_CODES.INVALID_API_KEY,
        message: 'Invalid department code',
      });
    }

    if (!department.isActive) {
      throw new UnauthorizedException({
        code: ERROR_CODES.INVALID_API_KEY,
        message: 'Department is inactive',
      });
    }

    const isValidApiKey = await HashUtil.comparePassword(apiKey, department.apiKeyHash || '');
    if (!isValidApiKey) {
      throw new UnauthorizedException({
        code: ERROR_CODES.INVALID_API_KEY,
        message: 'Invalid API key',
      });
    }

    const payload: JwtPayload = {
      sub: department.id,
      role: UserRole.DEPARTMENT,
      departmentCode: department.code,
    };

    return {
      accessToken: this.jwtService.sign(payload),
      department: {
        id: department.id,
        code: department.code,
        name: department.name,
      },
    };
  }

  /**
   * Mock DigiLocker login (for POC)
   * In production, this would integrate with actual DigiLocker OAuth
   */
  async digiLockerLogin(
    dto: DigiLockerLoginDto,
  ): Promise<{ accessToken: string; applicant: { id: string; name: string; email: string } }> {
    let applicant = await this.applicantsService.findByDigilockerId(dto.digilockerId);

    // Auto-create applicant if not exists (mock behavior)
    if (!applicant) {
      this.logger.log(`Creating new applicant for DigiLocker ID: ${dto.digilockerId}`);
      applicant = await this.applicantsService.create({
        digilockerId: dto.digilockerId,
        name: dto.name || 'DigiLocker User',
        email: dto.email || `${dto.digilockerId}@digilocker.gov.in`,
        phone: dto.phone,
      });
    }

    if (!applicant.isActive) {
      throw new UnauthorizedException({
        code: ERROR_CODES.UNAUTHORIZED,
        message: 'Applicant account is inactive',
      });
    }

    const payload: JwtPayload = {
      sub: applicant.id,
      email: applicant.email,
      role: UserRole.APPLICANT,
    };

    return {
      accessToken: this.jwtService.sign(payload),
      applicant: {
        id: applicant.id,
        name: applicant.name,
        email: applicant.email,
      },
    };
  }

  /**
   * Validate JWT token and return payload
   */
  async validateJwtPayload(payload: JwtPayload): Promise<JwtPayload> {
    if (payload.role === UserRole.DEPARTMENT && payload.departmentCode) {
      const department = await this.departmentsService.findByCode(payload.departmentCode);
      if (!department || !department.isActive) {
        throw new UnauthorizedException({
          code: ERROR_CODES.INVALID_TOKEN,
          message: 'Department no longer valid',
        });
      }
    } else if (payload.role === UserRole.APPLICANT) {
      const applicant = await this.applicantsService.findById(payload.sub);
      if (!applicant || !applicant.isActive) {
        throw new UnauthorizedException({
          code: ERROR_CODES.INVALID_TOKEN,
          message: 'Applicant account no longer valid',
        });
      }
    }

    return payload;
  }

  /**
   * Email/Password login for all user types (Admin, Department, Citizen)
   */
  async emailPasswordLogin(dto: EmailPasswordLoginDto): Promise<{
    accessToken: string;
    user: {
      id: string;
      email: string;
      name: string;
      role: string;
      walletAddress: string;
      departmentId?: string;
    };
  }> {
    const user = await this.usersService.findByEmailWithDepartment(dto.email);

    if (!user) {
      throw new UnauthorizedException({
        code: ERROR_CODES.INVALID_CREDENTIALS,
        message: 'Invalid email or password',
      });
    }

    if (!user.isActive) {
      throw new UnauthorizedException({
        code: ERROR_CODES.UNAUTHORIZED,
        message: 'User account is inactive',
      });
    }

    const isValidPassword = await bcrypt.compare(dto.password, user.passwordHash);
    if (!isValidPassword) {
      throw new UnauthorizedException({
        code: ERROR_CODES.INVALID_CREDENTIALS,
        message: 'Invalid email or password',
      });
    }

    // Update last login
    await this.usersService.updateLastLogin(user.id);

    const payload: JwtPayload = {
      sub: user.id,
      email: user.email,
      role: user.role as UserRole,
      departmentCode: (user as any).department?.code,
    };

    return {
      accessToken: this.jwtService.sign(payload),
      user: {
        id: user.id,
        email: user.email,
        name: user.name,
        role: user.role,
        walletAddress: user.walletAddress || '',
        departmentId: user.departmentId,
      },
    };
  }

  /**
   * Generate admin token (for internal use)
   */
  generateAdminToken(adminId: string): string {
    const payload: JwtPayload = {
      sub: adminId,
      role: UserRole.ADMIN,
    };
    return this.jwtService.sign(payload);
  }
}
