import {
  Injectable,
  Logger,
  OnApplicationBootstrap,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PrismaService } from '@core/database/prisma/prisma.service';

/**
 * SSO 配置 + 启动期 fail-fast 校验。
 *
 * 事实源：docs/modules/organization/06-data-model.md §「env 变量约定（v2.4 起）」
 *        docs/modules/organization/03-architecture.md §「校验清单」
 *
 * 校验规则（按 03-arch 校验清单）：
 *  1) AZURE_TENANT_ID 必须是 GUID（不接受 common / organizations / consumers）
 *  2) SSO_ALLOWED_DOMAINS 非空时 SSO_JIT_DEFAULT_ORG_ID 必填
 *  3) SSO_JIT_DEFAULT_ORG_ID 必须对应 deletedAt IS NULL 的 Organization
 *  任一失败 → 启动期 logger.error + process.exit(1)
 */
@Injectable()
export class SsoConfigService implements OnApplicationBootstrap {
  private readonly logger = new Logger(SsoConfigService.name);

  // 不能直接读 process.env —— 走 ConfigService 以便 test 覆盖
  // 解析后的运行时字段（ctor 内一次性 trim / split 后冻结为只读）
  public readonly tenantId: string;
  public readonly clientId: string;
  public readonly clientSecret: string;
  public readonly redirectUri: string;
  /** 已 trim / lower-case 的域名数组；空 = 关闭 JIT */
  public readonly allowedDomains: readonly string[];
  public readonly jitDefaultOrgId: string;

  constructor(
    private readonly config: ConfigService,
    private readonly prisma: PrismaService,
  ) {
    this.tenantId = (this.config.get<string>('AZURE_TENANT_ID') || '').trim();
    this.clientId = (this.config.get<string>('AZURE_CLIENT_ID') || '').trim();
    this.clientSecret = (this.config.get<string>('AZURE_CLIENT_SECRET') || '').trim();
    this.redirectUri = (this.config.get<string>('AZURE_REDIRECT_URI') || '').trim();
    this.jitDefaultOrgId = (this.config.get<string>('SSO_JIT_DEFAULT_ORG_ID') || '').trim();

    const raw = this.config.get<string>('SSO_ALLOWED_DOMAINS') || '';
    this.allowedDomains = Object.freeze(
      raw
        .split(',')
        .map((d) => d.trim().toLowerCase())
        .filter((d) => d.length > 0),
    );
  }

  /** 是否启用 SSO 通道（核心 env 都齐备时为 true） */
  get enabled(): boolean {
    return !!(this.tenantId && this.clientId && this.clientSecret && this.redirectUri);
  }

  /** email 域名是否在 JIT 白名单 */
  isDomainAllowed(email: string): boolean {
    if (!email || this.allowedDomains.length === 0) return false;
    const lower = email.trim().toLowerCase();
    const at = lower.lastIndexOf('@');
    if (at < 0) return false;
    const domain = lower.slice(at + 1);
    return this.allowedDomains.includes(domain);
  }

  async onApplicationBootstrap(): Promise<void> {
    try {
      await this.validate();
    } catch (err: any) {
      this.logger.error(`SSO 配置校验失败，进程退出：${err.message}`);
      // 按事实源（06-data-model §5 / 03-arch 校验清单）：任一失败立即 exit(1)
      // eslint-disable-next-line no-process-exit
      process.exit(1);
    }
  }

  /**
   * 启动期 fail-fast 校验。
   *
   * 仅在 enabled=true（核心 env 齐备）或 jitDefaultOrgId 有值时校验；
   * dev 模式三个 AZURE_* 全空时直接跳过（保留只跑密码登录的开发能力）。
   */
  async validate(): Promise<void> {
    // 1) 核心 env 完全为空 + 不开 JIT → 视为关闭 SSO 通道，跳过校验
    if (
      !this.tenantId &&
      !this.clientId &&
      !this.clientSecret &&
      !this.redirectUri &&
      this.allowedDomains.length === 0 &&
      !this.jitDefaultOrgId
    ) {
      this.logger.log('SSO 通道未配置，跳过启动期校验（仅密码登录可用）');
      return;
    }

    // 2) AZURE_TENANT_ID 必须 GUID（不接受 common / organizations / consumers）
    const guidRegex = /^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$/;
    if (!this.tenantId || !guidRegex.test(this.tenantId)) {
      throw new Error(
        `AZURE_TENANT_ID 必须为 GUID 格式（不接受 common / organizations / consumers）；当前值="${this.tenantId}"`,
      );
    }

    if (!this.clientId) {
      throw new Error('AZURE_CLIENT_ID 必填');
    }
    if (!this.clientSecret) {
      throw new Error('AZURE_CLIENT_SECRET 必填');
    }
    if (!this.redirectUri) {
      throw new Error('AZURE_REDIRECT_URI 必填');
    }

    // 3) SSO_ALLOWED_DOMAINS 非空 → SSO_JIT_DEFAULT_ORG_ID 必填 + DB 校验存在
    if (this.allowedDomains.length > 0) {
      if (!this.jitDefaultOrgId) {
        throw new Error(
          'SSO_ALLOWED_DOMAINS 非空时 SSO_JIT_DEFAULT_ORG_ID 必填',
        );
      }
      const org = await this.prisma.organization.findUnique({
        where: { id: this.jitDefaultOrgId },
      });
      if (!org || (org.deletedAt !== null && org.deletedAt !== undefined)) {
        throw new Error(
          `SSO_JIT_DEFAULT_ORG_ID="${this.jitDefaultOrgId}" 对应的 Organization 不存在或已软删（WHERE deletedAt IS NULL）`,
        );
      }
    }

    this.logger.log(
      `SSO 配置校验通过：tenant=${this.tenantId}, allowedDomains=[${this.allowedDomains.join(',')}], jitDefaultOrgId=${this.jitDefaultOrgId || '(disabled)'}`,
    );
  }
}
