import {
  Injectable,
  InternalServerErrorException,
  NotFoundException,
} from '@nestjs/common';
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
import { envs } from 'src/config';
import { PrismaService } from 'src/prisma/prisma.service';
import { UpsertNovasisPayConfigDto } from './dto/upsert-novasis-pay-config.dto';

const ALGORITHM = 'aes-256-cbc';
const IV_LENGTH = 16;

function getEncryptionKey(): Buffer {
  const raw = envs.novasisPayEncryptionKey ?? '';
  if (raw.length !== 32) {
    throw new InternalServerErrorException(
      'NOVASIS_PAY_ENCRYPTION_KEY debe tener exactamente 32 caracteres',
    );
  }
  return Buffer.from(raw, 'utf8');
}

function encrypt(text: string): string {
  const key = getEncryptionKey();
  const iv = randomBytes(IV_LENGTH);
  const cipher = createCipheriv(ALGORITHM, key, iv);
  const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
  return `${iv.toString('hex')}:${encrypted.toString('hex')}`;
}

function decrypt(encrypted: string): string {
  const key = getEncryptionKey();
  const [ivHex, dataHex] = encrypted.split(':');
  const iv = Buffer.from(ivHex, 'hex');
  const data = Buffer.from(dataHex, 'hex');
  const decipher = createDecipheriv(ALGORITHM, key, iv);
  const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
  return decrypted.toString('utf8');
}

function maskKey(key: string | null | undefined): string | null {
  if (!key) return null;
  if (key.length <= 12) return '••••';
  return `${key.slice(0, 8)}…${key.slice(-4)}`;
}

@Injectable()
export class NovasisPayConfigService {
  constructor(private readonly prisma: PrismaService) {}

  async upsertConfig(empresaId: string, dto: UpsertNovasisPayConfigDto) {
    const existing = await this.prisma.novasis_pay_config.findUnique({
      where: { empresa_id: empresaId },
    });

    let secretKeyEncrypted = existing?.secret_key_encrypted ?? null;
    if (dto.secret_key) {
      secretKeyEncrypted = encrypt(dto.secret_key);
    }

    const data = {
      merchant_id: dto.merchant_id ?? existing?.merchant_id ?? null,
      publishable_key: dto.publishable_key ?? existing?.publishable_key ?? null,
      secret_key_encrypted: secretKeyEncrypted,
      webhook_secret: dto.webhook_secret ?? existing?.webhook_secret ?? null,
      mode: dto.mode ?? existing?.mode ?? 'test',
      default_provider: dto.default_provider ?? existing?.default_provider ?? null,
      default_country: dto.default_country ?? existing?.default_country ?? 'PY',
      api_base_url:
        dto.api_base_url ?? existing?.api_base_url ?? 'https://api.pay.novasis.com/v1',
      checkout_base_url:
        dto.checkout_base_url ?? existing?.checkout_base_url ?? 'https://pay.novasis.com/checkout',
      cuenta_contable_por_liquidar:
        dto.cuenta_contable_por_liquidar ?? existing?.cuenta_contable_por_liquidar ?? null,
      cuenta_contable_banco:
        dto.cuenta_contable_banco ?? existing?.cuenta_contable_banco ?? null,
      cuenta_contable_comision:
        dto.cuenta_contable_comision ?? existing?.cuenta_contable_comision ?? null,
      activo: dto.activo ?? existing?.activo ?? false,
      qr_embebido: dto.qr_embebido ?? existing?.qr_embebido ?? false,
      updated_at: new Date(),
    };

    const cfg = await this.prisma.novasis_pay_config.upsert({
      where: { empresa_id: empresaId },
      create: { empresa_id: empresaId, ...data },
      update: data,
    });

    return this.toSafeConfig(cfg);
  }

  async getConfig(empresaId: string) {
    const cfg = await this.prisma.novasis_pay_config.findUnique({
      where: { empresa_id: empresaId },
    });
    if (!cfg) return null;
    return this.toSafeConfig(cfg);
  }

  async getSecretKey(empresaId: string): Promise<string> {
    const cfg = await this.prisma.novasis_pay_config.findUnique({
      where: { empresa_id: empresaId },
    });
    if (!cfg?.secret_key_encrypted) {
      throw new NotFoundException(
        'Configuración Novasis Pay no encontrada o sin secret key',
      );
    }
    return decrypt(cfg.secret_key_encrypted);
  }

  async requireActiveConfig(empresaId: string) {
    const cfg = await this.prisma.novasis_pay_config.findUnique({
      where: { empresa_id: empresaId },
    });
    if (!cfg || !cfg.activo || !cfg.secret_key_encrypted) {
      throw new NotFoundException(
        'Novasis Pay no está habilitado o configurado para esta empresa',
      );
    }
    return {
      merchantId: cfg.merchant_id,
      apiBaseUrl: cfg.api_base_url,
      checkoutBaseUrl: cfg.checkout_base_url,
      secretKey: decrypt(cfg.secret_key_encrypted),
      defaultProvider: cfg.default_provider,
      defaultCountry: cfg.default_country,
      mode: cfg.mode,
      qrEmbebido: cfg.qr_embebido,
    };
  }

  private toSafeConfig(cfg: {
    id: string;
    empresa_id: string;
    merchant_id: string | null;
    publishable_key: string | null;
    secret_key_encrypted: string | null;
    webhook_secret: string | null;
    mode: string;
    default_provider: string | null;
    default_country: string;
    api_base_url: string;
    checkout_base_url: string;
    cuenta_contable_por_liquidar: string | null;
    cuenta_contable_banco: string | null;
    cuenta_contable_comision: string | null;
    activo: boolean;
    qr_embebido: boolean;
    created_at: Date;
    updated_at: Date;
  }) {
    return {
      id: cfg.id,
      empresa_id: cfg.empresa_id,
      merchant_id: cfg.merchant_id,
      publishable_key: cfg.publishable_key,
      // Nunca devolvemos la clave en claro: sólo enmascarada para que la UI confirme "se guardó algo".
      secret_key_masked: maskKey(
        cfg.secret_key_encrypted ? 'sk_••••••••••••••••' : null,
      ),
      tiene_secret_key: !!cfg.secret_key_encrypted,
      tiene_webhook_secret: !!cfg.webhook_secret,
      mode: cfg.mode,
      default_provider: cfg.default_provider,
      default_country: cfg.default_country,
      api_base_url: cfg.api_base_url,
      checkout_base_url: cfg.checkout_base_url,
      cuenta_contable_por_liquidar: cfg.cuenta_contable_por_liquidar,
      cuenta_contable_banco: cfg.cuenta_contable_banco,
      cuenta_contable_comision: cfg.cuenta_contable_comision,
      activo: cfg.activo,
      qr_embebido: cfg.qr_embebido,
      created_at: cfg.created_at,
      updated_at: cfg.updated_at,
    };
  }
}
