import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Client } from 'ldapts';
import type { SearchOptions, Entry } from 'ldapts';

export interface AdUserData {
  // 基本信息
  mail: string;
  displayName: string;
  givenName?: string;
  sn?: string;
  cn: string;
  
  // 组织信息
  department?: string;
  title?: string;
  company?: string;
  physicalDeliveryOfficeName?: string;
  
  // 员工信息
  employeeID?: string;
  employeeNumber?: string;
  sAMAccountName: string;
  userPrincipalName: string;
  
  // 组织架构
  manager?: string;
  directReports?: string[];
  memberOf?: string[];
  
  // 其他
  proxyAddresses?: string[];
  distinguishedName: string;
  whenCreated?: string;
  whenChanged?: string;
  
  // 账号状态
  userAccountControl?: number;
  accountExpires?: string;
}

@Injectable()
export class AdDirectService implements OnModuleInit {
  private readonly logger = new Logger(AdDirectService.name);
  private client: Client | null = null;
  private enabled: boolean;
  
  // AD 配置
  private readonly url: string;
  private readonly baseDN: string;
  private readonly bindDN: string;
  private readonly bindPassword: string;
  private readonly usersDN: string;
  private readonly tlsEnabled: boolean;
  private readonly tlsRejectUnauthorized: boolean;

  // 字段映射配置
  private readonly fieldMapping: {
    email: string;
    displayName: string;
    username: string;
    employeeId: string;
    givenName: string;
    surname: string;
    cn: string;
    department: string;
    title: string;
    company: string;
    location: string;
    phone: string;
    mobile: string;
    manager: string;
    directReports: string;
    memberOf: string;
    proxyAddresses: string;
    distinguishedName: string;
    userPrincipalName: string;
    whenCreated: string;
    whenChanged: string;
    userAccountControl: string;
    accountExpires: string;
  };

  constructor(private configService: ConfigService) {
    // 检查是否启用 AD 直连
    this.enabled = this.configService.get<boolean>('AD_DIRECT_ENABLED', false);
    
    if (!this.enabled) {
      this.logger.warn('AD Direct connection is disabled. Set AD_DIRECT_ENABLED=true to enable.');
      return;
    }

    // 加载配置
    this.url = this.configService.get<string>('AD_URL', '');
    this.baseDN = this.configService.get<string>('AD_BASE_DN', '');
    this.bindDN = this.configService.get<string>('AD_BIND_DN', '');
    this.bindPassword = this.configService.get<string>('AD_BIND_PASSWORD', '');
    this.usersDN = this.configService.get<string>('AD_USERS_DN', this.baseDN);
    this.tlsEnabled = this.configService.get<boolean>('AD_TLS_ENABLED', true);
    this.tlsRejectUnauthorized = this.configService.get<boolean>('AD_TLS_REJECT_UNAUTHORIZED', false);

    // 加载字段映射配置（使用默认值）
    this.fieldMapping = {
      email: this.configService.get<string>('AD_FIELD_EMAIL', 'mail'),
      displayName: this.configService.get<string>('AD_FIELD_DISPLAY_NAME', 'displayName'),
      username: this.configService.get<string>('AD_FIELD_USERNAME', 'sAMAccountName'),
      employeeId: this.configService.get<string>('AD_FIELD_EMPLOYEE_ID', 'employeeID'),
      givenName: this.configService.get<string>('AD_FIELD_GIVEN_NAME', 'givenName'),
      surname: this.configService.get<string>('AD_FIELD_SURNAME', 'sn'),
      cn: this.configService.get<string>('AD_FIELD_CN', 'cn'),
      department: this.configService.get<string>('AD_FIELD_DEPARTMENT', 'department'),
      title: this.configService.get<string>('AD_FIELD_TITLE', 'title'),
      company: this.configService.get<string>('AD_FIELD_COMPANY', 'company'),
      location: this.configService.get<string>('AD_FIELD_LOCATION', 'physicalDeliveryOfficeName'),
      phone: this.configService.get<string>('AD_FIELD_PHONE', 'telephoneNumber'),
      mobile: this.configService.get<string>('AD_FIELD_MOBILE', 'mobile'),
      manager: this.configService.get<string>('AD_FIELD_MANAGER', 'manager'),
      directReports: this.configService.get<string>('AD_FIELD_DIRECT_REPORTS', 'directReports'),
      memberOf: this.configService.get<string>('AD_FIELD_MEMBER_OF', 'memberOf'),
      proxyAddresses: this.configService.get<string>('AD_FIELD_PROXY_ADDRESSES', 'proxyAddresses'),
      distinguishedName: this.configService.get<string>('AD_FIELD_DISTINGUISHED_NAME', 'distinguishedName'),
      userPrincipalName: this.configService.get<string>('AD_FIELD_USER_PRINCIPAL_NAME', 'userPrincipalName'),
      whenCreated: this.configService.get<string>('AD_FIELD_WHEN_CREATED', 'whenCreated'),
      whenChanged: this.configService.get<string>('AD_FIELD_WHEN_CHANGED', 'whenChanged'),
      userAccountControl: this.configService.get<string>('AD_FIELD_USER_ACCOUNT_CONTROL', 'userAccountControl'),
      accountExpires: this.configService.get<string>('AD_FIELD_ACCOUNT_EXPIRES', 'accountExpires'),
    };

    // 验证配置
    if (!this.url || !this.baseDN || !this.bindDN || !this.bindPassword) {
      this.logger.error('AD Direct connection configuration incomplete!');
      this.logger.error('Required: AD_URL, AD_BASE_DN, AD_BIND_DN, AD_BIND_PASSWORD');
      this.enabled = false;
    }
  }

  async onModuleInit() {
    if (this.enabled) {
      try {
        await this.connect();
        this.logger.log('✅ AD Direct connection initialized successfully');
      } catch (error) {
        this.logger.error('❌ Failed to initialize AD Direct connection:', error.message);
      }
    }
  }

  /**
   * 连接到 AD 服务器
   */
  private async connect(): Promise<void> {
    try {
      // 强制使用普通 LDAP（非 SSL）
      let connectionUrl = this.url;
      
      // 自动转换 ldaps:// 为 ldap://
      if (connectionUrl.startsWith('ldaps://')) {
        connectionUrl = connectionUrl.replace('ldaps://', 'ldap://').replace(':636', ':389');
        this.logger.warn(`⚠️  Converting LDAPS to plain LDAP: ${connectionUrl}`);
      }

      const clientOptions: any = {
        url: connectionUrl,
        timeout: 10000,
        connectTimeout: 10000,
        // 不使用 TLS
      };

      this.logger.log(`🔓 Using plain LDAP (no SSL/TLS): ${connectionUrl}`);

      this.client = new Client(clientOptions);

      // 绑定（认证）
      await this.client.bind(this.bindDN, this.bindPassword);
      this.logger.log(`✅ Connected to AD: ${connectionUrl}`);
    } catch (error) {
      this.logger.error('Failed to connect to AD:', error.message);
      throw error;
    }
  }

  /**
   * 确保连接可用
   */
  private async ensureConnection(): Promise<void> {
    if (!this.enabled) {
      throw new Error('AD Direct connection is not enabled');
    }

    if (!this.client) {
      await this.connect();
    }
  }

  /**
   * 搜索用户
   */
  async searchUsers(filter?: string, attributes?: string[]): Promise<AdUserData[]> {
    await this.ensureConnection();

    const defaultFilter = this.configService.get<string>(
      'AD_USER_FILTER',
      '(&(objectClass=user)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))'
    );

    const searchFilter = filter || defaultFilter;

    // 使用配置的字段映射
    const defaultAttributes = [
      this.fieldMapping.email,
      this.fieldMapping.displayName,
      this.fieldMapping.givenName,
      this.fieldMapping.surname,
      this.fieldMapping.cn,
      this.fieldMapping.department,
      this.fieldMapping.title,
      this.fieldMapping.company,
      this.fieldMapping.location,
      this.fieldMapping.employeeId,
      'employeeNumber', // 备用工号字段
      this.fieldMapping.username,
      this.fieldMapping.userPrincipalName,
      this.fieldMapping.phone,
      this.fieldMapping.mobile,
      this.fieldMapping.manager,
      this.fieldMapping.directReports,
      this.fieldMapping.memberOf,
      this.fieldMapping.proxyAddresses,
      this.fieldMapping.distinguishedName,
      this.fieldMapping.whenCreated,
      this.fieldMapping.whenChanged,
      this.fieldMapping.userAccountControl,
      this.fieldMapping.accountExpires,
    ];

    const searchOptions: SearchOptions = {
      scope: 'sub',
      filter: searchFilter,
      attributes: attributes || defaultAttributes,
      paged: true,
      sizeLimit: Number(this.configService.get('AD_SEARCH_SIZE_LIMIT', '1000')),
    };

    try {
      this.logger.log(`Searching AD: ${this.usersDN} with filter: ${searchFilter}`);
      
      const { searchEntries } = await this.client!.search(this.usersDN, searchOptions);
      
      this.logger.log(`Found ${searchEntries.length} entries from AD`);

      return searchEntries.map(entry => this.normalizeAdEntry(entry));
    } catch (error) {
      this.logger.error('AD search failed:', error.message);
      throw error;
    }
  }

  /**
   * 获取所有活跃用户
   */
  async getAllActiveUsers(): Promise<AdUserData[]> {
    const filter = this.configService.get<string>(
      'AD_USER_FILTER',
      '(&(objectClass=user)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))'
    );

    return this.searchUsers(filter);
  }

  /**
   * 按部门搜索用户
   */
  async getUsersByDepartment(department: string): Promise<AdUserData[]> {
    const filter = `(&(objectClass=user)(objectCategory=person)(department=${this.escapeLdapFilter(department)})(!(userAccountControl:1.2.840.113556.1.4.803:=2)))`;
    return this.searchUsers(filter);
  }

  /**
   * 按邮箱域名搜索用户
   */
  async getUsersByEmailDomain(domain: string): Promise<AdUserData[]> {
    const filter = `(&(objectClass=user)(objectCategory=person)(mail=*@${this.escapeLdapFilter(domain)})(!(userAccountControl:1.2.840.113556.1.4.803:=2)))`;
    return this.searchUsers(filter);
  }

  /**
   * 按自定义过滤器搜索用户
   */
  async getUsersByCustomFilter(customFilter: string): Promise<AdUserData[]> {
    return this.searchUsers(customFilter);
  }

  /**
   * 获取单个用户
   */
  async getUserByUsername(username: string): Promise<AdUserData | null> {
    const filter = `(&(objectClass=user)(objectCategory=person)(|(sAMAccountName=${this.escapeLdapFilter(username)})(mail=${this.escapeLdapFilter(username)})(userPrincipalName=${this.escapeLdapFilter(username)})))`;
    
    const users = await this.searchUsers(filter);
    return users.length > 0 ? users[0] : null;
  }

  /**
   * 规范化 AD 条目（使用字段映射）
   */
  private normalizeAdEntry(entry: Entry): AdUserData {
    const getValue = (fieldName: string, isArray = false): any => {
      const value = entry[fieldName];
      if (!value) return isArray ? [] : undefined;
      
      if (Array.isArray(value)) {
        return isArray ? value : value[0];
      }
      
      return isArray ? [value] : value;
    };

    // 提取主邮箱（使用映射的字段）
    const proxyAddresses = getValue(this.fieldMapping.proxyAddresses, true) || [];
    const primaryEmail = this.extractPrimaryEmail(
      proxyAddresses, 
      getValue(this.fieldMapping.email)
    );

    return {
      mail: primaryEmail,
      displayName: getValue(this.fieldMapping.displayName) || getValue(this.fieldMapping.cn),
      givenName: getValue(this.fieldMapping.givenName),
      sn: getValue(this.fieldMapping.surname),
      cn: getValue(this.fieldMapping.cn),
      department: getValue(this.fieldMapping.department),
      title: getValue(this.fieldMapping.title),
      company: getValue(this.fieldMapping.company),
      physicalDeliveryOfficeName: getValue(this.fieldMapping.location),
      employeeID: getValue(this.fieldMapping.employeeId) || getValue('employeeNumber'),
      employeeNumber: getValue('employeeNumber'),
      sAMAccountName: getValue(this.fieldMapping.username),
      userPrincipalName: getValue(this.fieldMapping.userPrincipalName),
      manager: getValue(this.fieldMapping.manager),
      directReports: getValue(this.fieldMapping.directReports, true),
      memberOf: getValue(this.fieldMapping.memberOf, true),
      proxyAddresses: proxyAddresses,
      distinguishedName: getValue(this.fieldMapping.distinguishedName),
      whenCreated: getValue(this.fieldMapping.whenCreated),
      whenChanged: getValue(this.fieldMapping.whenChanged),
      userAccountControl: parseInt(getValue(this.fieldMapping.userAccountControl)) || undefined,
      accountExpires: getValue(this.fieldMapping.accountExpires),
    };
  }

  /**
   * 从 proxyAddresses 提取主邮箱
   */
  private extractPrimaryEmail(proxyAddresses: string[], fallback: string): string {
    if (!proxyAddresses || proxyAddresses.length === 0) {
      return fallback || '';
    }

    // SMTP: 开头表示主邮箱（大写）
    const primary = proxyAddresses.find(addr => 
      typeof addr === 'string' && addr.startsWith('SMTP:')
    );
    
    if (primary) {
      return primary.substring(5);
    }

    // smtp: 开头表示备用邮箱（小写）
    const secondary = proxyAddresses.find(addr => 
      typeof addr === 'string' && addr.toLowerCase().startsWith('smtp:')
    );
    
    if (secondary) {
      return secondary.substring(5);
    }

    return fallback || '';
  }

  /**
   * LDAP 过滤器转义
   */
  private escapeLdapFilter(value: string): string {
    return value
      .replace(/\\/g, '\\5c')
      .replace(/\*/g, '\\2a')
      .replace(/\(/g, '\\28')
      .replace(/\)/g, '\\29')
      .replace(/\0/g, '\\00');
  }

  /**
   * 提取用户组名称
   */
  extractGroupNames(memberOf: string[]): string[] {
    return memberOf.map(dn => {
      const match = dn.match(/^CN=([^,]+)/i);
      return match ? match[1] : null;
    }).filter((name): name is string => name !== null);
  }

  /**
   * 检查用户是否属于特定组
   */
  isMemberOf(memberOf: string[], groupName: string): boolean {
    const groups = this.extractGroupNames(memberOf);
    return groups.some(g => g.toLowerCase().includes(groupName.toLowerCase()));
  }

  /**
   * 健康检查
   */
  async healthCheck(): Promise<boolean> {
    if (!this.enabled) {
      return false;
    }

    try {
      await this.ensureConnection();
      
      // 简单搜索测试
      const searchOptions: SearchOptions = {
        scope: 'base',
        filter: '(objectClass=*)',
        sizeLimit: 1,
      };

      await this.client!.search(this.baseDN, searchOptions);
      return true;
    } catch (error) {
      this.logger.error('AD health check failed:', error.message);
      return false;
    }
  }

  /**
   * 断开连接
   */
  async disconnect(): Promise<void> {
    if (this.client) {
      try {
        await this.client.unbind();
        this.logger.log('Disconnected from AD');
      } catch (error) {
        this.logger.error('Error disconnecting from AD:', error.message);
      } finally {
        this.client = null;
      }
    }
  }

  /**
   * 检查是否启用
   */
  isEnabled(): boolean {
    return this.enabled;
  }
}
