import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as soap from 'soap';
import * as path from 'path';
import { buildSapHeader, buildSapItem, PurchaseFormData, SapSyncResult } from './sap-field-mapping';

export type SapEnvironment = 'test' | 'production';

@Injectable()
export class SapPurchaseService {
  private readonly logger = new Logger(SapPurchaseService.name);
  private clients: Partial<Record<SapEnvironment, soap.Client>> = {};
  private initPromises: Partial<Record<SapEnvironment, Promise<soap.Client>>> = {};

  constructor(private configService: ConfigService) {}

  private getWsdlPath(env: SapEnvironment): string {
    const filename = env === 'production' ? 'purchase-req-prod.wsdl' : 'purchase-req-test.wsdl';
    return path.join(__dirname, 'wsdl', filename);
  }

  private getCredentials(env: SapEnvironment): { username: string; password: string; timeout: number } {
    const envPrefix = env === 'production' ? 'SAP_PRODUCTION' : 'SAP_TEST';
    return {
      username: this.configService.get<string>(`${envPrefix}_USERNAME`) || this.configService.get<string>('SAP_USERNAME', ''),
      password: this.configService.get<string>(`${envPrefix}_PASSWORD`) || this.configService.get<string>('SAP_PASSWORD', ''),
      timeout: this.configService.get<number>(`${envPrefix}_TIMEOUT`) || this.configService.get<number>('SAP_TIMEOUT', 30000),
    };
  }

  private async getClient(env: SapEnvironment): Promise<soap.Client> {
    if (this.clients[env]) return this.clients[env]!;

    // 防止并发初始化：复用同一个 promise
    if (!this.initPromises[env]) {
      this.initPromises[env] = this.initClient(env).finally(() => {
        delete this.initPromises[env];
      });
    }
    return this.initPromises[env]!;
  }

  private async initClient(env: SapEnvironment): Promise<soap.Client> {
    const wsdlPath = this.getWsdlPath(env);
    const { username, password, timeout } = this.getCredentials(env);

    this.logger.log(`初始化 SAP SOAP 客户端 [${env}]: wsdl=${path.basename(wsdlPath)} user=${username} timeout=${timeout}ms`);

    const client = await soap.createClientAsync(wsdlPath);

    if (username && password) {
      client.setSecurity(new soap.BasicAuthSecurity(username, password));
    }

    // 支持通过环境变量覆盖 WSDL 中的 endpoint（用于 SSH 隧道/代理场景）
    const endpointOverride = this.configService.get<string>(`SAP_${env.toUpperCase()}_ENDPOINT`);
    if (endpointOverride) {
      client.setEndpoint(endpointOverride);
      this.logger.log(`SAP endpoint 已覆盖 [${env}]: ${endpointOverride}`);
    }

    this.clients[env] = client;
    this.logger.log(`SAP SOAP 客户端 [${env}] 初始化完成`);
    return client;
  }

  async createPurchaseRequisition(data: PurchaseFormData, env: SapEnvironment): Promise<SapSyncResult> {
    try {
      this.logger.log(`开始创建 SAP 采购申请 [${env}]，流水号: ${data.serialNumber}`);

      const client = await this.getClient(env);
      const header = buildSapHeader(data);
      const item = buildSapItem(data);

      this.logger.log(`SAP Header: ${JSON.stringify(header)}`);
      this.logger.log(`SAP Item: ${JSON.stringify(item)}`);

      const args = {
        IM_HEADER: header,
        T_PUR_ITEM: { item: [item] },
        T_RET_MESSAGES: [],
      };

      const { timeout } = this.getCredentials(env);
      const [result] = await client.SIOS_PurchaseRequestionAsync(args, { timeout });

      return this.parseResponse(result, data.serialNumber);
    } catch (error: any) {
      this.logger.error(`创建 SAP 采购申请失败 [${env}]，流水号: ${data.serialNumber}: ${error.message}`);

      if (error.root?.Envelope?.Body?.Fault) {
        const fault = error.root.Envelope.Body.Fault;
        this.logger.error(`SOAP Fault: ${JSON.stringify(fault)}`);
      }

      return {
        success: false,
        alreadyExists: false,
        prNumber: null,
        messages: [{ type: 'E', message: error.message }],
        rawResponse: null,
      };
    }
  }

  private parseResponse(response: any, serialNumber: string): SapSyncResult {
    const prNumberRaw = response?.EX_PR_NUMBER;
    let prNumber: string | null = null;

    if (prNumberRaw) {
      const cleaned = String(prNumberRaw).trim();
      if (cleaned && cleaned !== '#' && !cleaned.startsWith('#')) {
        prNumber = cleaned;
      }
    }

    const messages: Array<{ type: string; message: string }> = [];
    let hasError = false;
    let hasSuccess = false;

    const retMessages = response?.T_RET_MESSAGES?.item || response?.T_RET_MESSAGES || [];
    const msgList = Array.isArray(retMessages) ? retMessages : [retMessages];

    for (const msg of msgList) {
      if (!msg) continue;
      const msgType = msg.MSG_TYPE || '';
      const msgText = msg.MESSAGE || '';
      messages.push({ type: msgType, message: msgText });

      if (msgType === 'E' || msgType === 'A') hasError = true;
      if (msgType === 'S') hasSuccess = true;
    }

    // 检测"已存在"场景：从错误消息中提取已有的 PR 号
    let alreadyExists = false;
    if (hasError && !prNumber) {
      for (const msg of messages) {
        // SAP 消息格式：Tracking number XXX already exists in PR Number NNNNN
        const match = msg.message.match(/already exists in PR\s*(?:Number)?\s*(\d+)/i);
        if (match) {
          prNumber = match[1].padStart(10, '0'); // SAP PR 号补齐 10 位
          alreadyExists = true;
          break;
        }
      }
    }

    const success = alreadyExists || (prNumber ? !hasError : (hasSuccess && !hasError));

    if (alreadyExists) {
      this.logger.log(`SAP 采购申请已存在，PR号: ${prNumber}，流水号: ${serialNumber}`);
    } else if (success) {
      this.logger.log(`SAP 采购申请创建成功，PR号: ${prNumber}，流水号: ${serialNumber}`);
    } else {
      this.logger.error(`SAP 采购申请创建失败，流水号: ${serialNumber}`);
      for (const msg of messages) {
        if (msg.type === 'E' || msg.type === 'A') {
          this.logger.error(`  [${msg.type}] ${msg.message}`);
        }
      }
    }

    for (const msg of messages) {
      this.logger.log(`  SAP 消息 [${msg.type}] ${msg.message}`);
    }

    return { success, alreadyExists, prNumber, messages, rawResponse: response };
  }
}
