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

// Agente HTTPS tolerante a SSL antigo (Windows Server)
const httpsAgent = new https.Agent({
  rejectUnauthorized: false,
  secureProtocol: 'TLS_method',
});

interface TokenCache {
  token: string;
  expiresAt: number;
}

interface ContraChequeRow {
  VINCULO: string;
  MATRICULA: string;
  NOME: string;
  CPF: string;
  ADMISSAO: string;
  OCUPACAO: string;
  RAZAOLEGAL: string;
  CNPJLEGAL: string;
  EVENTOS: Array<{
    NREVENTOM: string;
    VRREFFOLHA: string;
    VREVENTOFOLHA: string;
    IDTIPOEVENTO: string;
    NMEVENTOH: string;
  }>;
}

interface ColaboradoresRow {
  VINCULO: string;
  NOME: string;
  DTINICIO: string;
  DTFIM: string;
  NRESTRUTLEGAL: number;
}

@Injectable()
export class HcmService {
  private readonly logger = new Logger(HcmService.name);
  private readonly baseUrl: string;
  private readonly clientId: string;
  private readonly clientSecret: string;
  private tokenCache: TokenCache | null = null;
  private readonly TOKEN_TTL = 24 * 60 * 60 * 1000; // 24h

  constructor(private config: ConfigService) {
    this.baseUrl     = config.get('HCM_BASE_URL');
    this.clientId    = config.get('HCM_CLIENT_ID');
    this.clientSecret = config.get('HCM_CLIENT_SECRET');
  }

  // ── Autenticação com cache 24h ──────────────────────────────────────
  async getToken(): Promise<string> {
    const now = Date.now();
    if (this.tokenCache && now < this.tokenCache.expiresAt) {
      this.logger.debug('Token obtido do cache');
      return this.tokenCache.token;
    }

    this.logger.log('Solicitando novo token HCM...');
    const res = await axios.post(`${this.baseUrl}/getAcessToken`, {
      CLIENT_ID: this.clientId,
      CLIENT_SECRET: this.clientSecret,
    });

    const token = res.data?.access_token;
    if (!token) throw new HttpException('Token não retornado pela API HCM', 502);

    this.tokenCache = { token, expiresAt: now + this.TOKEN_TTL };
    this.logger.log('Novo token HCM obtido e cacheado por 24h');
    return token;
  }

  // ── Contra-cheques (folha) ──────────────────────────────────────────
  async getContraCheque(competencia: string, tipoMovimento = '1') {
    const token = await this.getToken();

    const res = await axios.post(
      `${this.baseUrl}/getContraCheque`,
      {
        requestType: 'Row',
        row: {
          NUMEROVINCULOINICIAL: '1',
          NUMEROVINCULOFINAL: '9999',
          COMPETENCIA: competencia,
          NRTIPOMOVIMENT: tipoMovimento,
        },
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'OAuth-Token': token,
          'OAuth-SecretId': this.clientSecret,
        },
      },
    );

    const funcionarios: ContraChequeRow[] =
      res.data?.dataset?.getContraCheque ?? [];

    // Processa e agrega por unidade (RAZAOLEGAL)
    return this.processarFolha(funcionarios);
  }

  // ── Colaboradores ───────────────────────────────────────────────────
  async getColaboradores(dtInicio: string, dtFim: string, nrEstrutura: number) {
    const token = await this.getToken();

    const res = await axios.post(
      `${this.baseUrl}/getColaboradores`,
      {
        requestType: 'Row',
        row: { DTINICIO: dtInicio, DTFIM: dtFim, NRESTRUTLEGAL: nrEstrutura },
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'OAuth-Token': token,
          'OAuth-SecretId': this.clientSecret,
        },
        httpsAgent,
      },
    );

    return res.data;
  }

  // ── Processamento interno ───────────────────────────────────────────
  private processarFolha(funcionarios: ContraChequeRow[]) {
    const processados = funcionarios.map((f) => {
      const proventos = f.EVENTOS.filter((e) => e.IDTIPOEVENTO === 'PROVENTO')
        .reduce((s, e) => s + parseFloat(e.VREVENTOFOLHA || '0'), 0);
      const descontos = f.EVENTOS.filter((e) => e.IDTIPOEVENTO === 'DESCONTO')
        .reduce((s, e) => s + parseFloat(e.VREVENTOFOLHA || '0'), 0);
      return { ...f, proventos, descontos, liquido: proventos - descontos };
    });

    // Agregar por unidade
    const porUnidade: Record<string, { razaoLegal: string; cnpj: string; proventos: number; descontos: number; liquido: number; headcount: number }> = {};
    processados.forEach((f) => {
      const key = f.CNPJLEGAL || f.RAZAOLEGAL;
      if (!porUnidade[key]) {
        porUnidade[key] = {
          razaoLegal: f.RAZAOLEGAL,
          cnpj: f.CNPJLEGAL,
          proventos: 0,
          descontos: 0,
          liquido: 0,
          headcount: 0,
        };
      }
      porUnidade[key].proventos  += f.proventos;
      porUnidade[key].descontos  += f.descontos;
      porUnidade[key].liquido    += f.liquido;
      porUnidade[key].headcount  += 1;
    });

    const totais = processados.reduce(
      (acc, f) => ({
        proventos: acc.proventos + f.proventos,
        descontos: acc.descontos + f.descontos,
        liquido:   acc.liquido   + f.liquido,
      }),
      { proventos: 0, descontos: 0, liquido: 0 },
    );

    return {
      totais: { ...totais, headcount: processados.length },
      unidades: Object.values(porUnidade),
      colaboradores: processados,
    };
  }
}
