import { Injectable, Logger } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from 'src/prisma/prisma.service';
import {
  descripcionIncidencia,
  firmaIncidencia,
  lineaOcurrencia,
  motivoCargable,
  Ocurrencia,
  prioridadIncidencia,
  tituloIncidencia,
} from './decision';
import { YoutrackClient } from './youtrack.client';

/**
 * Bandeja de salida de incidencias (plan §6.8). `evaluar` corre al cerrar un turno
 * NO_RESUELTO o con 👎: decide por código si el problema es de Novasis y lo encola.
 * El cron la drena contra YouTrack con reintentos; si YouTrack no responde, el chat
 * no se entera.
 */
const MAX_INTENTOS = 12;

export interface TurnoParaIncidencia {
  numero: number;
  empresaRuc: string | null;
  empresaRazonSocial: string | null;
  motivo: string | null;
  categoria: string | null;
}

@Injectable()
export class IncidenciasService {
  private readonly logger = new Logger(IncidenciasService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly youtrack: YoutrackClient,
  ) {}

  /** Nunca lanza: una falla acá no puede afectar la respuesta al cliente. */
  async evaluar(t: TurnoParaIncidencia): Promise<void> {
    try {
      if (!motivoCargable(t.motivo)) return;
      const firma = firmaIncidencia(t.motivo, t.categoria);
      const evento: Ocurrencia = {
        sop: t.numero,
        ruc: t.empresaRuc,
        razon_social: t.empresaRazonSocial,
        fecha: new Date().toISOString().slice(0, 16).replace('T', ' ') + ' UTC',
      };
      const existente = await this.prisma.incidencias.findUnique({ where: { firma } });
      if (!existente) {
        await this.prisma.incidencias.create({
          data: {
            firma,
            motivo: t.motivo,
            categoria: t.categoria,
            titulo: tituloIncidencia(t.motivo, t.categoria),
            prioridad: prioridadIncidencia(t.motivo),
            empresas: [t.empresaRuc].filter(Boolean) as string[],
            eventos: [evento as unknown as Prisma.InputJsonValue] as Prisma.InputJsonValue,
          },
        });
        return;
      }
      const eventos = [...leerEventos(existente.eventos), evento];
      const empresas = new Set((existente.empresas as string[]) ?? []);
      if (t.empresaRuc) empresas.add(t.empresaRuc);
      await this.prisma.incidencias.update({
        where: { firma },
        data: {
          ocurrencias: { increment: 1 },
          empresas: [...empresas],
          eventos: eventos as unknown as Prisma.InputJsonValue,
          updated_at: new Date(),
        },
      });
    } catch (err) {
      this.logger.error(`No se pudo encolar la incidencia del SOP-${t.numero}: ${(err as Error).message}`);
    }
  }

  /** Drena la bandeja: crea el issue si no existe y agrega un comentario por ocurrencia nueva. */
  async drenar(limite = 10): Promise<{ creadas: number; comentadas: number; fallas: number }> {
    const resumen = { creadas: 0, comentadas: 0, fallas: 0 };
    if (!this.youtrack.disponible) return resumen;
    const ahora = new Date();
    const pendientes = await this.prisma.incidencias.findMany({
      where: {
        intentos: { lt: MAX_INTENTOS },
        OR: [{ proximo_intento_at: null }, { proximo_intento_at: { lte: ahora } }],
      },
      orderBy: { updated_at: 'asc' },
      take: limite * 3,
    });
    let hechas = 0;
    for (const inc of pendientes) {
      const eventos = leerEventos(inc.eventos);
      if (inc.youtrack_id && eventos.length === 0) continue;
      if (hechas >= limite) break;
      hechas += 1;
      try {
        let idReadable = inc.youtrack_id;
        let porComentar = eventos;
        if (!idReadable) {
          const [primera, ...resto] = eventos;
          const creado = await this.youtrack.crearIssue(
            inc.titulo,
            descripcionIncidencia({
              motivo: inc.motivo as never,
              categoria: inc.categoria,
              firma: inc.firma,
              primera: primera ?? { sop: 0, ruc: null, razon_social: null, fecha: '' },
            }),
          );
          idReadable = creado.idReadable;
          await this.youtrack.comando(idReadable, `Priority ${inc.prioridad}`);
          porComentar = resto;
          resumen.creadas += 1;
        }
        for (const e of porComentar) {
          await this.youtrack.comentar(idReadable, `Nueva ocurrencia: ${lineaOcurrencia(e)}`);
          resumen.comentadas += 1;
        }
        await this.prisma.incidencias.update({
          where: { id: inc.id },
          data: {
            youtrack_id: idReadable,
            estado: 'CREADA',
            eventos: [] as unknown as Prisma.InputJsonValue,
            intentos: 0,
            ultimo_error: null,
            proximo_intento_at: null,
            updated_at: new Date(),
          },
        });
      } catch (err) {
        resumen.fallas += 1;
        const intentos = inc.intentos + 1;
        const esperaMin = Math.min(intentos * intentos, 120);
        await this.prisma.incidencias
          .update({
            where: { id: inc.id },
            data: {
              intentos,
              estado: inc.youtrack_id ? 'CREADA' : 'ERROR',
              ultimo_error: String((err as Error).message).slice(0, 500),
              proximo_intento_at: new Date(Date.now() + esperaMin * 60_000),
            },
          })
          .catch(() => undefined);
        this.logger.warn(`Incidencia ${inc.firma}: ${(err as Error).message}`);
      }
    }
    return resumen;
  }
}

function leerEventos(v: Prisma.JsonValue): Ocurrencia[] {
  return Array.isArray(v) ? (v as unknown as Ocurrencia[]) : [];
}
