import { Inject, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as crypto from 'crypto';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import * as path from 'path';
import { Logger } from 'winston';
import TaihuId, { GAdminTokenName } from './lib/taihu';

@Injectable()
export class AppService {
  private readonly externalPageSecretKey: string;
  private readonly externalPageExpireSeconds: number;

  constructor(
    private readonly config: ConfigService,
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
  ) {
    this.logger = this.logger.child({
      file: path.basename(__filename),
    });

    // External Page 配置
    this.externalPageSecretKey =
      this.config.get<string>('EXTERNAL_PAGE_SECRET') ||
      process.env.EXTERNAL_PAGE_SECRET ||
      'change-this-secret-in-production';

    this.externalPageExpireSeconds =
      this.config.get<number>('EXTERNAL_PAGE_EXPIRE_SECONDS') || 300;

    if (this.externalPageSecretKey === 'change-this-secret-in-production') {
      this.logger.warn(
        'external_page_secret_warning - 使用默认密钥，请在生产环境配置 EXTERNAL_PAGE_SECRET 环境变量',
      );
    }
  }

  getHello(): string {
    return 'Hello World!';
  }

  async loginCallback(req, res) {
    const { code, state } = req.query;

    const tokenInfo = await TaihuId.getToken(code);
    const userInfo = await TaihuId.getUserInfo(tokenInfo.access_token);

    const expiresIn = 7 * 24 * 3600;
    const { user_id, sub, name, email } = userInfo;
    const payload = { user_id, sub, name, email };
    const token = await TaihuId.sign(payload, expiresIn);

    // append token param for local development
    const redirectUrl = new URL(state);
    const selfUrl = new URL(process.env.TAIHU_CALLBACK || '');
    if (redirectUrl.hostname !== selfUrl.hostname) {
      redirectUrl.searchParams.set(GAdminTokenName, token);
    }

    res.cookie(GAdminTokenName, token, {
      maxAge: expiresIn * 1000,
      httpOnly: false,
    });
    res.status(302).redirect(redirectUrl.toString());
  }

  login(req, res) {
    const { redirect_url, ui_locales = 'zh-CN' } = req.query;

    // TODO: verify redirect_url domain

    const authorizeUrl = TaihuId.authorize(redirect_url, ui_locales);
    res.status(302).redirect(authorizeUrl);
  }

  logout(req, res) {
    const { redirect_url } = req.query;

    const logoutUrl = TaihuId.logout(redirect_url);
    res.cookie(GAdminTokenName, '', { maxAge: 0 });
    res.status(302).redirect(logoutUrl);
  }

  // ============ External Page 功能 ============

  /**
   * 生成带签名的外部页面 URL
   */
  generateExternalPageSignedUrl(
    domain: string,
    pageCode: string,
    userid: string,
  ): { url: string; expiresIn: number } {
    const timestamp = Date.now();
    const data = `${domain}:${pageCode}:${userid}:${timestamp}`;

    const signature = crypto
      .createHmac('sha256', this.externalPageSecretKey)
      .update(data)
      .digest('hex');

    let pagePath: string;
    let pageQuery: string;

    const queryIndex = pageCode.indexOf('?');
    if (queryIndex !== -1) {
      pagePath = pageCode.substring(0, queryIndex);
      pageQuery = pageCode.substring(queryIndex + 1);
    } else {
      pagePath = pageCode;
      pageQuery = '';
    }

    const signParams = `_t=${timestamp}&_s=${signature}&_u=${encodeURIComponent(
      userid,
    )}`;

    let url: string;
    if (pageQuery) {
      url = `https://oit-erp-gtdr.woa.com/${domain}/${pagePath}?${pageQuery}&${signParams}`;
    } else {
      url = `https://oit-erp-gtdr.woa.com/${domain}/${pagePath}?${signParams}`;
    }

    this.logger.info(
      `external_page_sign_url - 生成签名URL - domain: ${domain}, pageCode: ${pageCode}, userid: ${userid}`,
    );

    return {
      url,
      expiresIn: this.externalPageExpireSeconds,
    };
  }

  /**
   * 验证外部页面签名
   */
  verifyExternalPageSignature(
    domain: string,
    pageCode: string,
    userid: string,
    timestamp: string,
    signature: string,
  ): { valid: boolean; reason?: string } {
    const ts = parseInt(timestamp, 10);
    if (isNaN(ts)) {
      return { valid: false, reason: 'invalid timestamp format' };
    }

    const now = Date.now();
    const age = now - ts;

    if (age < -5000) {
      return { valid: false, reason: 'timestamp in future' };
    }

    if (age > this.externalPageExpireSeconds * 1000) {
      return { valid: false, reason: 'signature expired' };
    }

    const data = `${domain}:${pageCode}:${userid}:${timestamp}`;
    const expectedSignature = crypto
      .createHmac('sha256', this.externalPageSecretKey)
      .update(data)
      .digest('hex');

    if (signature.length !== expectedSignature.length) {
      return { valid: false, reason: 'invalid signature' };
    }

    try {
      const signatureBuffer = Buffer.from(signature, 'hex');
      const expectedBuffer = Buffer.from(expectedSignature, 'hex');

      if (
        !crypto.timingSafeEqual(
          new Uint8Array(signatureBuffer),
          new Uint8Array(expectedBuffer),
        )
      ) {
        return { valid: false, reason: 'invalid signature' };
      }
    } catch {
      return { valid: false, reason: 'invalid signature format' };
    }

    return { valid: true };
  }
}
