import "server-only";

import { LicitationProcedureType, type Prisma } from "@prisma/client";
import { LICITAYA_MUNICIPALITY_CODE } from "@/lib/licitations/constants";
import { prisma } from "@/lib/prisma";

type LicitationQueryParams = {
  state?: string | null;
  municipality?: string | null;
  procedureType?: string | null;
  q?: string | null;
  minAmount?: string | null;
  maxAmount?: string | null;
  dateFrom?: string | null;
  dateTo?: string | null;
  includeClosed?: boolean;
  take?: number;
  skip?: number;
};

function parseNumber(value: string | null | undefined) {
  if (!value) {
    return null;
  }

  const parsed = Number.parseFloat(value);
  return Number.isFinite(parsed) ? parsed : null;
}

function parseDate(value: string | null | undefined) {
  if (!value) {
    return null;
  }

  const parsed = new Date(value);
  return Number.isNaN(parsed.getTime()) ? null : parsed;
}

function parseProcedureType(value: string | null | undefined) {
  if (!value) {
    return null;
  }

  const normalized = value.trim().toUpperCase();

  if (normalized in LicitationProcedureType) {
    return normalized as LicitationProcedureType;
  }

  return null;
}

export async function listMunicipalities(filters?: { state?: string | null }) {
  return prisma.municipality.findMany({
    where: {
      isActive: true,
      municipalityCode: {
        not: LICITAYA_MUNICIPALITY_CODE,
      },
      ...(filters?.state ? { stateCode: filters.state } : {}),
    },
    orderBy: [{ stateName: "asc" }, { municipalityName: "asc" }],
    select: {
      id: true,
      stateCode: true,
      stateName: true,
      municipalityCode: true,
      municipalityName: true,
      openPortalUrl: true,
      openPortalType: true,
      pntSubjectId: true,
      backupUrl: true,
    },
  });
}

export async function searchLicitations(params: LicitationQueryParams) {
  const minAmount = parseNumber(params.minAmount);
  const maxAmount = parseNumber(params.maxAmount);
  const dateFrom = parseDate(params.dateFrom);
  const dateTo = parseDate(params.dateTo);
  const procedureType = parseProcedureType(params.procedureType);
  const q = params.q?.trim();
  const includeClosed = params.includeClosed === true;
  const now = new Date();

  const andClauses: Prisma.LicitationWhereInput[] = [];

  if (!includeClosed) {
    andClauses.push({ OR: [{ isOpen: true }, { closingDate: { gte: now } }] });
  }

  if (procedureType) {
    andClauses.push({ procedureType });
  }

  if (minAmount != null || maxAmount != null) {
    andClauses.push({
      amount: {
        ...(minAmount != null ? { gte: minAmount } : {}),
        ...(maxAmount != null ? { lte: maxAmount } : {}),
      },
    });
  }

  if (dateFrom || dateTo) {
    andClauses.push({
      publishDate: {
        ...(dateFrom ? { gte: dateFrom } : {}),
        ...(dateTo ? { lte: dateTo } : {}),
      },
    });
  }

  if (q) {
    andClauses.push({
      OR: [
        { title: { contains: q, mode: "insensitive" } },
        { description: { contains: q, mode: "insensitive" } },
        { supplierAwarded: { contains: q, mode: "insensitive" } },
        { tenderCode: { contains: q, mode: "insensitive" } },
      ],
    });
  }

  const where: Prisma.LicitationWhereInput = {
    municipality: {
      isActive: true,
      ...(params.state ? { stateCode: params.state } : {}),
      ...(params.municipality ? { municipalityCode: params.municipality } : {}),
    },
    ...(andClauses.length > 0 ? { AND: andClauses } : {}),
  };

  const [total, records] = await Promise.all([
    prisma.licitation.count({ where }),
    prisma.licitation.findMany({
      where,
      orderBy: [{ isOpen: "desc" }, { closingDate: "asc" }, { publishDate: "desc" }, { createdAt: "desc" }],
      take: params.take ?? 50,
      skip: params.skip ?? 0,
      include: {
        municipality: {
          select: {
            id: true,
            stateCode: true,
            stateName: true,
            municipalityCode: true,
            municipalityName: true,
          },
        },
      },
    }),
  ]);

  return {
    total,
    records,
  };
}
