import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { EntraService, EntraUser } from '../entra/entra.service';
import { IamSyncInProgressException, IamManagerLoopException } from '../exceptions';
import { randomUUID as uuidv4 } from 'crypto';

export interface SyncResult {
  success: boolean;
  totalUsers: number;
  syncedUsers: number;
  createdUsers: number;
  updatedUsers: number;
  skippedUsers: number;
  conflictUsers: number;
  protectedTerminatedUsers: number; // TERMINATED users not reactivated
  errors: string[];
  duration: number;
  timestamp: Date;
}

@Injectable()
export class SyncService {
  private readonly logger = new Logger(SyncService.name);
  private isSyncing = false;
  private lastSyncResult: SyncResult | null = null;

  constructor(
    private prisma: PrismaService,
    private entraService: EntraService,
    private configService: ConfigService,
  ) {}

  /**
   * Trigger sync from Entra ID
   * Automatically uses group sync if AZURE_ENTRA_SYNC_GROUP_ID is configured
   */
  async syncFromEntra(): Promise<SyncResult> {
    if (this.isSyncing) {
      throw new IamSyncInProgressException();
    }

    this.isSyncing = true;
    const startTime = Date.now();
    const errors: string[] = [];
    let createdUsers = 0;
    let updatedUsers = 0;
    let skippedUsers = 0;
    let conflictUsers = 0;
    let protectedTerminatedUsers = 0;

    try {
      this.logger.log('🔄 Starting Entra ID user synchronization...');

      if (!this.entraService.isEnabled()) {
        throw new Error('Entra ID is not configured. Please set AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET in .env');
      }

      // Phase 1: Get users from Entra ID
      // Check if group sync is configured
      const syncGroupId = this.entraService.getSyncGroupId();
      let entraUsers;
      
      if (syncGroupId) {
        this.logger.log(`🎯 Syncing users from configured group: ${syncGroupId}`);
        entraUsers = await this.entraService.getGroupMembers(syncGroupId);
      } else {
        this.logger.log('📥 Syncing all users (no group filter configured)...');
        entraUsers = await this.entraService.getAllUsers();
      }
      this.logger.log(`Found ${entraUsers.length} users in Entra ID`);

      // Phase 2: Sync each user (without manager relationship)
      this.logger.log('📝 Phase 1: Syncing user data...');
      for (const entraUser of entraUsers) {
        try {
          const result = await this.syncSingleUser(entraUser);
          if (result === 'created') {
            createdUsers++;
          } else if (result === 'updated') {
            updatedUsers++;
          } else if (result === 'protected_terminated') {
            protectedTerminatedUsers++;
            skippedUsers++;
          } else {
            skippedUsers++;
          }
        } catch (error: any) {
          if (error.message.includes('本地用户')) {
            conflictUsers++;
          }
          errors.push(`${entraUser.mail}: ${error.message}`);
          this.logger.error(`Failed to sync user ${entraUser.mail}:`, error.message);
        }
      }

      // Phase 3: Update manager relationships
      this.logger.log('🔗 Phase 2: Updating manager relationships...');
      let managersUpdated = 0;
      for (const entraUser of entraUsers) {
        try {
          if (entraUser.manager?.mail) {
            const updated = await this.updateUserManager(entraUser);
            if (updated) managersUpdated++;
          }
        } catch (error: any) {
          this.logger.warn(`Failed to update manager for ${entraUser.mail}:`, error.message);
        }
      }
      this.logger.log(`✅ Updated ${managersUpdated} manager relationships`);

      const duration = Date.now() - startTime;
      const result: SyncResult = {
        success: errors.length === 0,
        totalUsers: entraUsers.length,
        syncedUsers: createdUsers + updatedUsers,
        createdUsers,
        updatedUsers,
        skippedUsers,
        conflictUsers,
        protectedTerminatedUsers,
        errors,
        duration,
        timestamp: new Date(),
      };

      this.lastSyncResult = result;
      this.logger.log(
        `✅ Sync completed: ${createdUsers} created, ${updatedUsers} updated, ${skippedUsers} skipped, ${conflictUsers} conflicts, ${protectedTerminatedUsers} protected in ${duration}ms`,
      );

      return result;
    } catch (error: any) {
      this.logger.error('❌ Sync failed:', error.message);
      const duration = Date.now() - startTime;
      const result: SyncResult = {
        success: false,
        totalUsers: 0,
        syncedUsers: 0,
        createdUsers: 0,
        updatedUsers: 0,
        skippedUsers: 0,
        conflictUsers: 0,
        protectedTerminatedUsers: 0,
        errors: [error.message],
        duration,
        timestamp: new Date(),
      };
      this.lastSyncResult = result;
      return result;
    } finally {
      this.isSyncing = false;
    }
  }

  /**
   * Sync single user from Entra ID
   * Enhanced with externalId support and TERMINATED user protection
   */
  private async syncSingleUser(
    entraUser: EntraUser,
  ): Promise<'created' | 'updated' | 'skipped' | 'protected_terminated'> {
    const email = entraUser.mail?.toLowerCase() || entraUser.userPrincipalName.toLowerCase();
    const username = entraUser.userPrincipalName.split('@')[0];
    const externalId = entraUser.id; // Entra ID's objectId

    // Priority 1: Find by externalId (most reliable)
    let existingUser = await this.prisma.user.findFirst({
      where: {
        externalId,
        externalSource: 'ENTRA',
      },
    });

    // Priority 2: Find by email if not found by externalId
    if (!existingUser) {
      existingUser = await this.prisma.user.findUnique({
        where: { email },
      });
    }

    // TERMINATED user protection: Do not auto-reactivate
    if (existingUser && existingUser.status === 'TERMINATED') {
      this.logger.log(`⚠️  Skipping TERMINATED user: ${email} (manual reactivation required)`);
      return 'protected_terminated';
    }

    // Conflict: email exists but is a local user (never synced)
    if (existingUser && existingUser.source === 'LOCAL' && !existingUser.ldapSyncedAt) {
      const errorMsg = `邮箱 ${email} 已存在本地用户，无法从 Entra ID 同步。请检查是否有重复账号或联系管理员处理。`;
      this.logger.warn(`⚠️  ${errorMsg}`);
      throw new Error(errorMsg);
    }

    // ✅ 同步职位
    let positionId: string | null = null;
    if (entraUser.jobTitle) {
      const position = await this.syncPosition(entraUser.jobTitle);
      positionId = position?.id || null;
    }

    // Determine account status
    const accountStatus: 'ACTIVE' | 'INACTIVE' = entraUser.accountEnabled ? 'ACTIVE' : 'INACTIVE';

    // Map region from Entra officeLocation
    const region = this.mapOfficeLocationToRegion(entraUser.officeLocation);

    // Prepare user data
    const userData = {
      username,
      email,
      displayName: entraUser.displayName,
      ldapDn: `entra:${entraUser.id}`,
      employeeId: entraUser.employeeId || null,
      phone: entraUser.mobilePhone || entraUser.businessPhones?.[0] || null,
      status: accountStatus,
      source: 'ENTRA' as const,
      externalId,
      externalSource: 'ENTRA',
      defaultRegion: region,
    };

    // Prepare metadata
    const metadata = {
      entraId: entraUser.id,
      userPrincipalName: entraUser.userPrincipalName,
      manager: entraUser.manager
        ? {
            id: entraUser.manager.id,
            displayName: entraUser.manager.displayName,
            mail: entraUser.manager.mail,
            userPrincipalName: entraUser.manager.userPrincipalName,
          }
        : undefined,
      jobTitle: entraUser.jobTitle,
      department: entraUser.department,
      officeLocation: entraUser.officeLocation,
      givenName: entraUser.givenName,
      surname: entraUser.surname,
      userType: entraUser.userType,
    };

    if (!existingUser) {
      // Create new user
      await this.prisma.user.create({
        data: {
          id: uuidv4(),
          ...userData,
          ldapSyncedAt: new Date(),
          metadata,
          passwordHash: null as any,
        },
      });

      const statusIcon = accountStatus === 'ACTIVE' ? '✅' : '❌';
      this.logger.log(`${statusIcon} Created new user from Entra ID: ${email} [${accountStatus}]`);
      return 'created';
    } else {
      // Check if there are changes
      const hasChanges = this.hasUserChanges(existingUser, userData);

      if (!hasChanges) {
        // 更新 Position（如果用户已经有部门）
        await this.updateUserPosition(existingUser.id, positionId);
        return 'skipped';
      }

      // Update existing user (bind externalId if not already bound)
      await this.prisma.user.update({
        where: { id: existingUser.id },
        data: {
          ...userData,
          ldapSyncedAt: new Date(),
          metadata,
        } as any,
      });

      // ✅ 更新 Position（如果用户已经有部门）
      await this.updateUserPosition(existingUser.id, positionId);

      this.logger.log(`✅ Updated user from Entra ID: ${email}`);
      return 'updated';
    }
  }

  /**
   * Check if user data has changed
   */
  private hasUserChanges(existing: any, newData: any): boolean {
    const fieldsToCompare = [
      'username',
      'displayName',
      'employeeId',
      'status',
      'defaultRegion', // Schema 字段名
      'externalId',
      // Note: departmentId 和 positionId 已移至 UserDepartment 关系表，不在此比较
    ];

    for (const field of fieldsToCompare) {
      if (existing[field] !== newData[field]) {
        return true;
      }
    }

    return false;
  }

  /**
   * Map Entra officeLocation to region
   */
  private mapOfficeLocationToRegion(officeLocation?: string): string {
    if (!officeLocation) return 'CN';

    const locationLower = officeLocation.toLowerCase();

    // Region mapping based on common patterns
    const regionMap: Record<string, string> = {
      'china': 'CN',
      'shanghai': 'CN',
      'beijing': 'CN',
      'shenzhen': 'CN',
      'guangzhou': 'CN',
      'us': 'US',
      'usa': 'US',
      'america': 'US',
      'new york': 'US',
      'san francisco': 'US',
      'seattle': 'US',
      'europe': 'EU',
      'london': 'EU',
      'paris': 'EU',
      'berlin': 'EU',
      'singapore': 'APAC',
      'tokyo': 'APAC',
      'sydney': 'APAC',
      'asia': 'APAC',
    };

    for (const [keyword, region] of Object.entries(regionMap)) {
      if (locationLower.includes(keyword)) {
        return region;
      }
    }

    return 'CN'; // Default
  }

  /**
   * Update user manager relationship with loop detection
   */
  private async updateUserManager(entraUser: EntraUser): Promise<boolean> {
    const email = entraUser.mail?.toLowerCase() || entraUser.userPrincipalName.toLowerCase();

    if (!entraUser.manager?.mail) {
      return false;
    }

    // ✅ 启用 Manager 同步：查找用户和 Manager
    const user = await this.prisma.user.findUnique({
      where: { email },
      include: {
        departmentMemberships: {
          where: { leftAt: null },
          include: { department: true },
        },
      },
    });

    if (!user) {
      this.logger.debug(`User ${email} not found, skipping manager sync`);
      return false;
    }

    // 查找 Manager
    const managerEmail = entraUser.manager.mail.toLowerCase();
    const manager = await this.prisma.user.findUnique({
      where: { email: managerEmail },
    });

    if (!manager) {
      this.logger.debug(`Manager ${managerEmail} not found for user ${email}, will be synced later`);
      return false;
    }

    // ✅ 改进：更新用户在所有部门的 Manager 关系
    const memberships = (user as any).departmentMemberships || [];
    
    if (memberships.length === 0) {
      this.logger.debug(`User ${email} has no department memberships, skipping manager sync`);
      return false;
    }

    let updatedCount = 0;
    let skippedCount = 0;

    // 遍历用户的所有部门归属
    for (const membership of memberships) {
      // 检查是否会造成循环引用（在该部门内）
      const wouldLoop = await this.wouldCreateLoop(user.id, manager.id, membership.departmentId);
      if (wouldLoop) {
        this.logger.warn(`⚠️  Manager loop detected for ${email} in department ${membership.department.name}, skipping`);
        skippedCount++;
        continue;
      }

      // 只更新 managerId 为 NULL 或与当前 manager 不同的记录
      if (membership.managerId !== manager.id) {
        await this.prisma.userDepartment.update({
          where: { id: membership.id },
          data: { managerId: manager.id },
        });
        updatedCount++;
        this.logger.debug(`✅ Updated manager for ${email} in department ${membership.department.name} to ${managerEmail}`);
      }
    }

    if (updatedCount > 0) {
      this.logger.debug(`✅ Updated manager for ${email} in ${updatedCount} department(s), skipped ${skippedCount}`);
      return true;
    }

    return false;
  }

  /**
   * Check if setting manager would create a loop
   */
  private async wouldCreateLoop(userId: string, managerId: string, departmentId: string): Promise<boolean> {
    // 检查是否会形成循环：A的上级是B，B的上级是C，...，最终回到A
    let currentManagerId: string | null = managerId;
    const visited = new Set<string>([userId]);
    const maxDepth = 20;
    let depth = 0;

    while (currentManagerId && depth < maxDepth) {
      if (visited.has(currentManagerId)) {
        return true; // 发现循环
      }

      visited.add(currentManagerId);

      // 查找当前 Manager 的上级（在同一部门内）
      const managerMembership: any = await this.prisma.userDepartment.findFirst({
        where: {
          userId: currentManagerId,
          departmentId,
          leftAt: null,
        },
      });

      currentManagerId = managerMembership?.managerId || null;
      depth++;
    }

    return false;
  }

  /**
   * Sync position
   */
  private async syncPosition(title: string) {
    if (!title || title.trim() === '') {
      return null;
    }

    try {
      const code = title.trim().toUpperCase().replace(/\s+/g, '_');

      return await this.prisma.position.upsert({
        where: { code },
        update: { name: title.trim() },
        create: {
          id: uuidv4(),
          code,
          name: title.trim(),
          description: `从 Entra ID 自动同步: ${title}`,
        },
      });
    } catch (error: any) {
      this.logger.error(`Failed to sync position ${title}:`, error.message);
      return null;
    }
  }

  /**
   * ✅ 更新用户的职位信息（只更新已有部门的职位）
   */
  private async updateUserPosition(
    userId: string,
    positionId: string | null,
  ): Promise<void> {
    try {
      // 查找用户的主部门关联
      const primaryMembership = await this.prisma.userDepartment.findFirst({
        where: {
          userId,
          isPrimary: true,
          leftAt: null,
        },
      });

      if (primaryMembership && positionId !== undefined && positionId !== primaryMembership.positionId) {
        // 只更新现有主部门的职位信息
        await this.prisma.userDepartment.update({
          where: { id: primaryMembership.id },
          data: { positionId },
        });
        this.logger.debug(`✅ Updated position for user ${userId}`);
      }
    } catch (error: any) {
      this.logger.error(`Failed to update position for user ${userId}:`, error.message);
    }
  }

  /**
   * Get sync status
   */
  getSyncStatus() {
    return {
      isSyncing: this.isSyncing,
      lastSyncTime: this.lastSyncResult?.timestamp || null,
      lastSyncResult: this.lastSyncResult,
    };
  }

  /**
   * Get organization statistics
   */
  async getOrganizationStats() {
    const [
      totalUsers,
      activeUsers,
      entraUsers,
      localUsers,
      terminatedUsers,
      totalDepartments,
      totalPositions,
    ] = await Promise.all([
      this.prisma.user.count({ where: { deletedAt: null } }),
      this.prisma.user.count({ where: { status: 'ACTIVE', deletedAt: null } }),
      this.prisma.user.count({ where: { source: 'ENTRA', deletedAt: null } }),
      this.prisma.user.count({ where: { source: 'LOCAL', deletedAt: null } }),
      this.prisma.user.count({ where: { status: 'TERMINATED', deletedAt: null } }),
      this.prisma.department.count({ where: { deletedAt: null } }),
      this.prisma.position.count({ where: { deletedAt: null } }),
    ]);

    return {
      users: {
        total: totalUsers,
        active: activeUsers,
        fromEntra: entraUsers,
        local: localUsers,
        terminated: terminatedUsers,
      },
      departments: totalDepartments,
      positions: totalPositions,
      lastSync: this.lastSyncResult?.timestamp || null,
    };
  }
}
