import "server-only";

import type { Prisma } from "@prisma/client";
import { prisma } from "@/lib/prisma";
import type {
  ContractAiExtractionOutput,
  ContractCreateInput,
  ContractDeliverableView,
  ContractKpiSnapshot,
  ContractListFilters,
  ContractPaymentView,
  ContractRecordView,
} from "@/lib/contracts/types";

function toNumber(value: Prisma.Decimal | number | string | null | undefined) {
  if (value === null || value === undefined) {
    return null;
  }

  const numeric = typeof value === "number" ? value : Number(value);
  return Number.isFinite(numeric) ? Number(numeric) : null;
}

function toIso(value: Date | null | undefined) {
  return value ? value.toISOString() : null;
}

export function isContractOperational(args: {
  status: "ACTIVE" | "COMPLETED" | "PAUSED" | "CANCELLED";
  endDate: Date | null;
  now?: Date;
}) {
  if (args.status !== "ACTIVE") {
    return false;
  }

  if (!args.endDate) {
    return true;
  }

  const now = args.now ?? new Date();
  return args.endDate.getTime() >= now.getTime();
}

function isDeliverableOverdue(input: {
  status: "PENDING" | "DELIVERED" | "APPROVED" | "REJECTED" | "OVERDUE";
  dueDate: Date | null;
  now: Date;
}) {
  if (input.status === "OVERDUE") {
    return true;
  }

  if (input.status !== "PENDING" || !input.dueDate) {
    return false;
  }

  return input.dueDate.getTime() < input.now.getTime();
}

function mapDeliverable(
  row: {
    id: string;
    contractId: string;
    title: string;
    dueDate: Date | null;
    amountLinked: Prisma.Decimal | null;
    status: "PENDING" | "DELIVERED" | "APPROVED" | "REJECTED" | "OVERDUE";
    deliveredAt: Date | null;
    approvedAt: Date | null;
    notes: string;
    createdAt: Date;
    updatedAt: Date;
  },
  now: Date,
): ContractDeliverableView {
  return {
    id: row.id,
    contractId: row.contractId,
    title: row.title,
    dueDate: toIso(row.dueDate),
    amountLinked: toNumber(row.amountLinked),
    status: row.status,
    deliveredAt: toIso(row.deliveredAt),
    approvedAt: toIso(row.approvedAt),
    notes: row.notes,
    isOverdue: isDeliverableOverdue({
      status: row.status,
      dueDate: row.dueDate,
      now,
    }),
    createdAt: row.createdAt.toISOString(),
    updatedAt: row.updatedAt.toISOString(),
  };
}

function mapPayment(row: {
  id: string;
  contractId: string;
  amount: Prisma.Decimal;
  paymentDate: Date;
  invoiceNumber: string | null;
  concept: string;
  status: "REGISTERED" | "CONFIRMED" | "DISPUTED";
  createdAt: Date;
  updatedAt: Date;
}): ContractPaymentView {
  return {
    id: row.id,
    contractId: row.contractId,
    amount: toNumber(row.amount) ?? 0,
    paymentDate: row.paymentDate.toISOString(),
    invoiceNumber: row.invoiceNumber,
    concept: row.concept,
    status: row.status,
    createdAt: row.createdAt.toISOString(),
    updatedAt: row.updatedAt.toISOString(),
  };
}

function computeProgress(deliverables: ContractDeliverableView[]) {
  const total = deliverables.length;

  if (total === 0) {
    return {
      completed: 0,
      total: 0,
      percent: 0,
    };
  }

  const completed = deliverables.filter((item) => item.status === "DELIVERED" || item.status === "APPROVED").length;
  const percent = Math.round((completed / total) * 100);

  return {
    completed,
    total,
    percent,
  };
}

export function computeContractKpis(contracts: ContractRecordView[]): ContractKpiSnapshot {
  const activeContracts = contracts.filter((item) => item.isActiveOperational).length;
  let totalCollected = 0;
  let pendingDeliverables = 0;
  let overdueDeliverables = 0;

  for (const contract of contracts) {
    for (const payment of contract.payments) {
      if (payment.status === "CONFIRMED") {
        totalCollected += payment.amount;
      }
    }

    for (const deliverable of contract.deliverables) {
      if (deliverable.status === "PENDING") {
        pendingDeliverables += 1;
      }

      if (deliverable.isOverdue) {
        overdueDeliverables += 1;
      }
    }
  }

  return {
    activeContracts,
    totalCollected: Number(totalCollected.toFixed(2)),
    pendingDeliverables,
    overdueDeliverables,
  };
}

export async function listContractsForUser(userId: string, filters: ContractListFilters = {}, now = new Date()) {
  const where: Prisma.ContractRecordWhereInput = {
    userId,
  };

  if (filters.status) {
    where.status = filters.status;
  }

  if (filters.q) {
    where.OR = [
      { title: { contains: filters.q, mode: "insensitive" } },
      { counterpartyEntity: { contains: filters.q, mode: "insensitive" } },
      { contractNumber: { contains: filters.q, mode: "insensitive" } },
      { contractType: { contains: filters.q, mode: "insensitive" } },
    ];
  }

  const orderBy: Prisma.ContractRecordOrderByWithRelationInput[] =
    filters.sort === "endDate"
      ? [{ endDate: "asc" }, { updatedAt: "desc" }]
      : filters.sort === "createdAt"
        ? [{ createdAt: "desc" }]
        : [{ updatedAt: "desc" }];

  const rows = await prisma.contractRecord.findMany({
    where,
    orderBy,
    include: {
      deliverables: {
        orderBy: [{ dueDate: "asc" }, { createdAt: "asc" }],
      },
      payments: {
        orderBy: [{ paymentDate: "desc" }, { createdAt: "desc" }],
      },
      documents: {
        orderBy: [{ createdAt: "desc" }],
      },
    },
    take: 200,
  });

  return rows.map<ContractRecordView>((row) => {
    const deliverables = row.deliverables.map((item) => mapDeliverable(item, now));

    return {
      id: row.id,
      sourceProposalId: row.sourceProposalId,
      title: row.title,
      counterpartyEntity: row.counterpartyEntity,
      contractNumber: row.contractNumber,
      contractType: row.contractType,
      startDate: toIso(row.startDate),
      endDate: toIso(row.endDate),
      totalAmount: toNumber(row.totalAmount),
      currency: row.currency,
      status: row.status,
      description: row.description,
      progress: computeProgress(deliverables),
      isActiveOperational: isContractOperational({
        status: row.status,
        endDate: row.endDate,
        now,
      }),
      createdAt: row.createdAt.toISOString(),
      updatedAt: row.updatedAt.toISOString(),
      deliverables,
      payments: row.payments.map((item) => mapPayment(item)),
      documents: row.documents.map((item) => ({
        id: item.id,
        contractId: item.contractId,
        fileName: item.fileName,
        filePath: item.filePath,
        mimeType: item.mimeType,
        sizeBytes: item.sizeBytes,
        checksumSha256: item.checksumSha256,
        kind: item.kind,
        createdAt: item.createdAt.toISOString(),
      })),
    };
  });
}

export async function getContractsKpisForUser(userId: string, now = new Date()) {
  const contracts = await listContractsForUser(userId, {}, now);
  return computeContractKpis(contracts);
}

export function contractCreateInputFromExtraction(args: {
  extraction: ContractAiExtractionOutput;
  fallbackTitle?: string;
}): ContractCreateInput {
  const { extraction } = args;

  return {
    title: extraction.fields.title ?? args.fallbackTitle ?? "Contrato sin titulo",
    counterpartyEntity: extraction.fields.counterpartyEntity ?? "Entidad contratante por validar",
    contractNumber: extraction.fields.contractNumber,
    contractType: extraction.fields.contractType ?? "General",
    startDate: extraction.fields.startDate,
    endDate: extraction.fields.endDate,
    totalAmount: extraction.fields.totalAmount,
    currency: extraction.fields.currency ?? "MXN",
    description: extraction.fields.description ?? "",
    status: "ACTIVE",
    sourceProposalId: null,
  };
}
