import { Injectable, Logger, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as https from 'https';

export interface LdapUserData {
  // 基本信息
  mail: string; // 主邮箱
  displayName: string;
  givenName: string;
  sn: string; // 姓
  cn: string; // 全名
  
  // 组织信息
  department?: string;
  title?: string;
  company?: string;
  physicalDeliveryOfficeName?: string; // 办公地点
  
  // 员工信息
  employeeID?: string;
  sAMAccountName: string; // AD 账号
  userPrincipalName: string;
  
  // 组织架构
  manager?: string; // DN
  directReports?: string[]; // DNs
  memberOf?: string[]; // 用户组 DNs
  
  // 其他邮箱
  proxyAddresses?: string[];
  
  // 元数据
  distinguishedName: string;
  whenCreated?: string;
  whenChanged?: string;
}

@Injectable()
export class LdapService {
  private readonly logger = new Logger(LdapService.name);
  private readonly ldapApiToken: string;
  private readonly ldapApiHost: string;
  private readonly ldapApiPort: number;

  constructor(private configService: ConfigService) {
    this.ldapApiToken = this.configService.get<string>('LDAP_API_TOKEN') || '';
    this.ldapApiHost = this.configService.get<string>('LDAP_API_HOST', 'qa-ldapapi.ffau.to');
    this.ldapApiPort = this.configService.get<number>('LDAP_API_PORT', 443);
    
    if (!this.ldapApiToken) {
      this.logger.warn('LDAP_API_TOKEN not configured. LDAP authentication will fail.');
    }
  }

  /**
   * 验证用户凭据并返回用户信息
   */
  async authenticate(username: string, password: string): Promise<LdapUserData> {
    try {
      this.logger.log(`Authenticating user: ${username}`);
      
      const userData = await this.callLdapApi('/login', {
        username,
        password,
      });

      if (!userData) {
        throw new UnauthorizedException('Invalid LDAP credentials');
      }

      return this.normalizeLdapData(userData);
    } catch (error) {
      this.logger.error(`LDAP authentication failed for ${username}:`, error.message);
      throw new UnauthorizedException('Authentication failed');
    }
  }

  /**
   * 健康检查
   */
  async healthCheck(): Promise<boolean> {
    try {
      const response = await this.callLdapApi('/health', null, 'GET');
      return !!response;
    } catch (error) {
      this.logger.error('LDAP health check failed:', error.message);
      return false;
    }
  }

  /**
   * 调用 LDAP API
   */
  private async callLdapApi(
    path: string,
    data: any = null,
    method: 'GET' | 'POST' = 'POST',
  ): Promise<any> {
    return new Promise((resolve, reject) => {
      const postData = data ? JSON.stringify(data) : null;

      const options: https.RequestOptions = {
        hostname: this.ldapApiHost,
        port: this.ldapApiPort,
        path,
        method,
        headers: {
          'Authorization': this.ldapApiToken,
          ...(postData && {
            'Content-Type': 'application/json',
            'Content-Length': Buffer.byteLength(postData),
          }),
        },
      };

      const req = https.request(options, (res) => {
        let responseData = '';

        res.on('data', (chunk) => {
          responseData += chunk;
        });

        res.on('end', () => {
          if (res.statusCode === 200) {
            try {
              const parsed = JSON.parse(responseData);
              resolve(parsed);
            } catch (e) {
              resolve(responseData);
            }
          } else {
            reject(new Error(`LDAP API returned ${res.statusCode}: ${responseData}`));
          }
        });
      });

      req.on('error', (error) => {
        reject(error);
      });

      if (postData) {
        req.write(postData);
      }

      req.end();
    });
  }

  /**
   * 规范化 LDAP 返回的数据
   */
  private normalizeLdapData(rawData: any): LdapUserData {
    // 提取主邮箱
    const primaryEmail = this.extractPrimaryEmail(rawData.proxyAddresses, rawData.mail);
    
    return {
      mail: primaryEmail,
      displayName: rawData.displayName?.trim() || rawData.cn,
      givenName: rawData.givenName,
      sn: rawData.sn,
      cn: rawData.cn,
      department: rawData.department,
      title: rawData.title,
      company: rawData.company,
      physicalDeliveryOfficeName: rawData.physicalDeliveryOfficeName,
      employeeID: rawData.employeeID,
      sAMAccountName: rawData.sAMAccountName,
      userPrincipalName: rawData.userPrincipalName,
      manager: rawData.manager,
      directReports: rawData.directReports || [],
      memberOf: rawData.memberOf || [],
      proxyAddresses: rawData.proxyAddresses || [],
      distinguishedName: rawData.distinguishedName,
      whenCreated: rawData.whenCreated,
      whenChanged: rawData.whenChanged,
    };
  }

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

    // 查找 SMTP: 开头的（大写表示主邮箱）
    const primary = proxyAddresses.find(addr => addr.startsWith('SMTP:'));
    if (primary) {
      return primary.substring(5); // 移除 'SMTP:' 前缀
    }

    // 查找 smtp: 开头的
    const secondary = proxyAddresses.find(addr => addr.startsWith('smtp:'));
    if (secondary) {
      return secondary.substring(5);
    }

    return fallback;
  }

  /**
   * 提取用户组简称（用于权限映射）
   */
  extractGroupNames(memberOf: string[]): string[] {
    return memberOf.map(dn => {
      // 从 "CN=Group Name,OU=..." 中提取 "Group Name"
      const match = dn.match(/^CN=([^,]+)/);
      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()));
  }

  /**
   * 提取管理员标识（从 DN 中提取用户名）
   */
  extractUsernameFromDn(dn: string): string | null {
    if (!dn) return null;
    const match = dn.match(/^CN=([^,]+)/);
    return match ? match[1] : null;
  }

  /**
   * 搜索用户（支持批量查询）
   * @param filter LDAP 过滤器，例如: "(objectClass=user)"
   * @param attributes 需要返回的属性
   * @param searchBase 搜索基准 DN
   */
  async searchUsers(
    filter?: string,
    attributes?: string[],
    searchBase?: string,
  ): Promise<LdapUserData[]> {
    try {
      this.logger.log(`Searching LDAP users with filter: ${filter || 'all active users'}`);
      
      const requestData: any = {};
      
      if (filter) {
        requestData.filter = filter;
      }
      
      if (attributes && attributes.length > 0) {
        requestData.attributes = attributes;
      }
      
      if (searchBase) {
        requestData.searchBase = searchBase;
      }
      
      const response = await this.callLdapApi('/search', requestData, 'POST');
      
      if (!response || !Array.isArray(response)) {
        this.logger.warn('LDAP search returned no results or invalid format');
        return [];
      }
      
      // 规范化所有用户数据
      return response.map(userData => this.normalizeLdapData(userData));
    } catch (error) {
      this.logger.error('LDAP user search failed:', error.message);
      throw error;
    }
  }

  /**
   * 获取所有活跃用户（根据配置的过滤规则）
   */
  async getAllActiveUsers(): Promise<LdapUserData[]> {
    const filter = this.configService.get<string>(
      'LDAP_USER_FILTER',
      '(&(objectClass=user)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))',
    );
    
    const searchBase = this.configService.get<string>('LDAP_SEARCH_BASE');
    
    return this.searchUsers(filter, undefined, searchBase);
  }

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

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

  /**
   * 获取用户详细信息（通过用户名）
   */
  async getUserByUsername(username: string): Promise<LdapUserData | null> {
    try {
      const filter = `(&(objectClass=user)(objectCategory=person)(|(sAMAccountName=${username})(mail=${username})(userPrincipalName=${username})))`;
      const users = await this.searchUsers(filter);
      return users.length > 0 ? users[0] : null;
    } catch (error) {
      this.logger.error(`Failed to get user ${username}:`, error.message);
      return null;
    }
  }
}
