import { NextResponse } from "next/server";
import { EterraClient } from "@/modules/parcel-sync/services/eterra-client";
import { getSessionCredentials } from "@/modules/parcel-sync/services/session-store";
import { prisma } from "@/core/storage/prisma";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

type Body = {
  siruta?: string;
  search?: string; // cadastral number(s), comma or newline separated
  username?: string;
  password?: string;
  workspacePk?: number; // county workspace PK — if provided, skips resolution
};

/* ------------------------------------------------------------------ */
/*  Workspace (county) lookup cache                                    */
/* ------------------------------------------------------------------ */

const globalRef = globalThis as {
  __eterraWorkspaceCache?: Map<string, number>;
};
const workspaceCache =
  globalRef.__eterraWorkspaceCache ?? new Map<string, number>();
globalRef.__eterraWorkspaceCache = workspaceCache;

/**
 * Resolve eTerra workspace ID for a given SIRUTA.
 *
 * Strategy: Query 1 feature from TERENURI_ACTIVE ArcGIS layer for this
 * SIRUTA, read the WORKSPACE_ID attribute.
 *
 * Uses `listLayer()` (not `listLayerByWhere`) so the admin field name
 * (ADMIN_UNIT_ID, SIRUTA, UAT_ID…) is auto-discovered from layer metadata.
 *
 * SIRUTA ≠ eTerra nomenPk, so nomenclature API lookups don't help.
 */
async function resolveWorkspace(
  client: EterraClient,
  siruta: string,
): Promise<number | null> {
  const cached = workspaceCache.get(siruta);
  if (cached !== undefined) return cached;

  try {
    // listLayer auto-discovers the correct admin field via buildWhere
    const features = await client.listLayer(
      {
        id: "TERENURI_ACTIVE",
        name: "TERENURI_ACTIVE",
        endpoint: "aut",
        whereTemplate: "{{adminField}}={{siruta}} AND IS_ACTIVE=1",
      },
      siruta,
      { limit: 1, outFields: "WORKSPACE_ID" },
    );

    const wsId = features?.[0]?.attributes?.WORKSPACE_ID;
    console.log(
      "[resolveWorkspace] ArcGIS WORKSPACE_ID for",
      siruta,
      "→",
      wsId,
    );
    if (wsId != null) {
      const numWs = Number(wsId);
      if (Number.isFinite(numWs)) {
        workspaceCache.set(siruta, numWs);
        // Persist to DB for future fast lookups
        persistWorkspace(siruta, numWs);
        return numWs;
      }
    }
  } catch (e) {
    console.log(
      "[resolveWorkspace] ArcGIS query failed:",
      e instanceof Error ? e.message : e,
    );
  }

  return null;
}

/** Fire-and-forget: save WORKSPACE_ID to GisUat row */
function persistWorkspace(siruta: string, workspacePk: number) {
  prisma.gisUat
    .upsert({
      where: { siruta },
      update: { workspacePk },
      create: { siruta, name: siruta, workspacePk },
    })
    .catch(() => {});
}

/* ------------------------------------------------------------------ */
/*  Helper formatters (same logic as export-bundle magic mode)         */
/* ------------------------------------------------------------------ */

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function formatAddress(item?: any) {
  const addresses = item?.immovableAddresses ?? [];
  if (addresses.length === 0) return "";

  // Build from ALL addresses (some parcels have multiple)
  const formatted: string[] = [];
  for (const entry of addresses) {
    const address = entry?.address ?? entry;
    if (!address) continue;

    const parts: string[] = [];

    // Street: dictionaryItem.name = type ("Strada"), name = actual name ("DIANEI")
    const streetObj = address.street;
    if (streetObj) {
      const streetType =
        typeof streetObj === "string"
          ? ""
          : (streetObj?.dictionaryItem?.name ?? "");
      const streetName =
        typeof streetObj === "string" ? streetObj : (streetObj?.name ?? "");
      if (streetType && streetName) {
        parts.push(`${streetType} ${streetName}`);
      } else if (streetName) {
        parts.push(`Str. ${streetName}`);
      }
    }

    // postalNo is often the house number in eTerra
    const houseNo = address.postalNo ?? address.buildingNo ?? null;
    if (houseNo) parts.push(`Nr. ${houseNo}`);

    // Building details
    if (address.buildingSectionNo)
      parts.push(`Bl. ${address.buildingSectionNo}`);
    if (address.buildingEntryNo) parts.push(`Sc. ${address.buildingEntryNo}`);
    if (address.buildingFloorNo) parts.push(`Et. ${address.buildingFloorNo}`);
    if (address.buildingUnitNo) parts.push(`Ap. ${address.buildingUnitNo}`);

    // Locality
    const localityName =
      typeof address.locality === "string"
        ? address.locality
        : (address.locality?.name ?? "");
    if (localityName) parts.push(localityName);

    // County
    const countyName =
      typeof address.county === "string"
        ? address.county
        : (address.county?.name ?? "");
    if (countyName) parts.push(`Jud. ${countyName}`);

    // Postal code
    if (address.postalCode) parts.push(`Cod ${address.postalCode}`);

    if (parts.length > 0) {
      formatted.push(parts.join(", "));
    } else if (address.addressDescription) {
      // Fall back to description only if no structured fields found
      const desc = String(address.addressDescription).trim();
      if (desc.length > 2 && !desc.includes("[object")) {
        formatted.push(desc);
      }
    }
  }

  // If we still have nothing, try addressDescription from first entry
  if (formatted.length === 0) {
    const desc =
      addresses[0]?.address?.addressDescription ??
      addresses[0]?.addressDescription;
    if (desc) {
      const s = String(desc).trim();
      if (s.length > 2 && !s.includes("[object")) return s;
    }
  }

  return [...new Set(formatted)].join(" | ");
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function normalizeIntravilan(values: string[]) {
  const normalized = values
    .map((v) =>
      String(v ?? "")
        .trim()
        .toLowerCase(),
    )
    .filter(Boolean);
  const unique = new Set(normalized);
  if (!unique.size) return "";
  if (unique.size === 1)
    return unique.has("da") ? "Da" : unique.has("nu") ? "Nu" : "Mixt";
  return "Mixt";
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function formatCategories(entries: any[]) {
  const map = new Map<string, number>();
  for (const entry of entries) {
    const key = String(entry?.categorieFolosinta ?? "").trim();
    if (!key) continue;
    const area = Number(entry?.suprafata ?? 0);
    map.set(key, (map.get(key) ?? 0) + (Number.isFinite(area) ? area : 0));
  }
  return Array.from(map.entries())
    .map(([k, a]) => `${k}:${a.toFixed(2).replace(/\.00$/, "")}`)
    .join("; ");
}

/* ------------------------------------------------------------------ */
/*  Route handler                                                      */
/* ------------------------------------------------------------------ */

export type ParcelDetail = {
  nrCad: string;
  nrCF: string;
  nrCFVechi: string;
  nrTopo: string;
  intravilan: string;
  categorieFolosinta: string;
  adresa: string;
  proprietari: string;
  proprietariActuali: string;
  proprietariVechi: string;
  suprafata: number | null;
  solicitant: string;
  immovablePk: string;
};

/**
 * POST /api/eterra/search
 *
 * Search eTerra by cadastral number using the application API
 * (same as the eTerra web UI). Returns full parcel details:
 * nr. cadastral, CF, topo, intravilan, categorii folosință,
 * adresă, proprietari.
 *
 * Accepts one or more cadastral numbers (comma/newline separated).
 */
export async function POST(req: Request) {
  try {
    const body = (await req.json()) as Body;
    const siruta = String(body.siruta ?? "").trim();
    const rawSearch = (body.search ?? "").trim();

    if (!siruta || !/^\d+$/.test(siruta)) {
      return NextResponse.json(
        { error: "SIRUTA obligatoriu" },
        { status: 400 },
      );
    }
    if (!rawSearch) {
      return NextResponse.json(
        { error: "Număr cadastral obligatoriu" },
        { status: 400 },
      );
    }

    // Parse multiple cadastral numbers (comma, newline, space separated)
    const cadNumbers = rawSearch
      .split(/[\s,;\n]+/)
      .map((s) => s.trim())
      .filter(Boolean);

    if (cadNumbers.length === 0) {
      return NextResponse.json(
        { error: "Număr cadastral obligatoriu" },
        { status: 400 },
      );
    }

    // Credential chain: body > session > env
    const session = getSessionCredentials();
    const username = String(
      body.username || session?.username || process.env.ETERRA_USERNAME || "",
    ).trim();
    const password = String(
      body.password || session?.password || process.env.ETERRA_PASSWORD || "",
    ).trim();

    if (!username || !password) {
      return NextResponse.json(
        { error: "Conectează-te la eTerra mai întâi." },
        { status: 401 },
      );
    }

    const client = await EterraClient.create(username, password);

    // Workspace resolution chain: body → DB → ArcGIS layer query
    let workspaceId = body.workspacePk ?? null;
    if (!workspaceId || !Number.isFinite(workspaceId)) {
      try {
        const dbUat = await prisma.gisUat.findUnique({
          where: { siruta },
          select: { workspacePk: true },
        });
        if (dbUat?.workspacePk) workspaceId = dbUat.workspacePk;
      } catch {
        // DB lookup failed
      }
    }
    if (!workspaceId || !Number.isFinite(workspaceId)) {
      workspaceId = await resolveWorkspace(client, siruta);
    }
    if (!workspaceId) {
      return NextResponse.json(
        { error: "Nu s-a putut determina județul pentru UAT-ul selectat." },
        { status: 400 },
      );
    }
    console.log("[search] siruta:", siruta, "workspaceId:", workspaceId);

    const results: ParcelDetail[] = [];

    for (const cadNr of cadNumbers) {
      try {
        // 1. Search immovable by identifier (exact match)
        const immResponse = await client.searchImmovableByIdentifier(
          workspaceId,
          siruta,
          cadNr,
        );

        const items = immResponse?.content ?? [];
        if (items.length === 0) {
          // No result — add placeholder so user knows it wasn't found
          results.push({
            nrCad: cadNr,
            nrCF: "",
            nrCFVechi: "",
            nrTopo: "",
            intravilan: "",
            categorieFolosinta: "",
            adresa: "",
            proprietari: "",
            proprietariActuali: "",
            proprietariVechi: "",
            suprafata: null,
            solicitant: "",
            immovablePk: "",
          });
          continue;
        }

        for (const item of items) {
          const immPk = item?.immovablePk;
          const immPkStr = String(immPk ?? "");

          // Basic data from immovable list
          let nrCF = String(item?.paperLbNo ?? item?.paperCadNo ?? "");
          let nrCFVechi = "";
          let nrTopo = String(item?.topNo ?? item?.paperCadNo ?? "");
          let addressText = formatAddress(item);
          let proprietariActuali: string[] = [];
          let proprietariVechi: string[] = [];
          let solicitant = "";
          let intravilan = "";
          let categorie = "";
          let suprafata: number | null = null;

          // Area: use measuredArea first, then legalArea as fallback
          for (const areaField of [
            item?.measuredArea,
            item?.legalArea,
            item?.area,
            item?.areaValue,
          ]) {
            if (areaField != null) {
              const parsed = Number(areaField);
              if (Number.isFinite(parsed) && parsed > 0) {
                suprafata = parsed;
                break;
              }
            }
          }

          // 2. Fetch documentation data (CF, proprietari)
          if (immPk) {
            try {
              const docResponse = await client.fetchDocumentationData(
                workspaceId,
                [immPk],
              );

              // Extract doc details
              const docImm = (docResponse?.immovables ?? []).find(
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                (d: any) => String(d?.immovablePk) === immPkStr,
              );
              if (docImm) {
                if (docImm.landbookIE) {
                  const oldCF = nrCF;
                  nrCF = String(docImm.landbookIE);
                  if (oldCF && oldCF !== nrCF) nrCFVechi = oldCF;
                }
                if (docImm.topNo) nrTopo = String(docImm.topNo);
                // Try measuredArea/legalArea from doc too
                for (const af of [
                  docImm.measuredArea,
                  docImm.legalArea,
                  docImm.area,
                ]) {
                  if (af != null) {
                    const docArea = Number(af);
                    if (Number.isFinite(docArea) && docArea > 0) {
                      suprafata = docArea;
                      break;
                    }
                  }
                }
              }

              // Extract owners from partTwoRegs — separate active vs cancelled
              // Tree: C (cerere) → A (act) → I (inscription) → P (person)
              // nodeStatus === -1 on an "I" node means it's radiated.
              // Walk up from each "P" to its parent "I", check nodeStatus.
              const activeOwners: string[] = [];
              const cancelledOwners: string[] = [];
              // eslint-disable-next-line @typescript-eslint/no-explicit-any
              const regs: any[] = docResponse?.partTwoRegs ?? [];

              // Build nodeId → entry map for tree traversal
              // eslint-disable-next-line @typescript-eslint/no-explicit-any
              const nodeMap = new Map<number, any>();
              for (const reg of regs) {
                if (reg?.nodeId != null) nodeMap.set(Number(reg.nodeId), reg);
              }

              // Check if an entry or any ancestor "I" inscription is radiated
              // nodeStatus: -1 = radiated, 0 = active, 2 = pending
              // eslint-disable-next-line @typescript-eslint/no-explicit-any
              const isRadiated = (entry: any, depth = 0): boolean => {
                if (!entry || depth > 10) return false;
                // nodeStatus === -1 means radiated/cancelled
                if (entry?.nodeStatus === -1) return true;
                // Walk up to parent
                const pid = entry?.parentId;
                if (pid != null) {
                  const parent = nodeMap.get(Number(pid));
                  if (parent) return isRadiated(parent, depth + 1);
                }
                return false;
              };

              for (const reg of regs) {
                if (
                  String(reg?.nodeType ?? "").toUpperCase() !== "P" ||
                  !reg?.nodeName
                )
                  continue;
                const name = String(reg.nodeName).trim();
                if (!name) continue;
                if (isRadiated(reg)) {
                  cancelledOwners.push(name);
                } else {
                  activeOwners.push(name);
                }
              }
              proprietariActuali = Array.from(new Set(activeOwners));
              proprietariVechi = Array.from(new Set(cancelledOwners)).filter(
                (n) => !proprietariActuali.includes(n),
              );
            } catch {
              // Documentation fetch failed — continue with basic data
            }
          }

          // 3. Fetch parcel details (area, intravilan, useCategory) — direct endpoint
          if (immPk) {
            try {
              const parcels = await client.fetchImmovableParcelDetails(
                workspaceId,
                immPk,
                1,
                100,
              );
              if (Array.isArray(parcels) && parcels.length > 0) {
                // Sum area from all parcels
                let totalArea = 0;
                const intraVals: string[] = [];
                const catMap = new Map<string, number>();
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                for (const p of parcels as any[]) {
                  const a = Number(p?.area ?? 0);
                  if (Number.isFinite(a) && a > 0) totalArea += a;
                  if (p?.intravilan) intraVals.push(String(p.intravilan));
                  const cat = String(p?.useCategory ?? "").trim();
                  if (cat) {
                    catMap.set(
                      cat,
                      (catMap.get(cat) ?? 0) + (Number.isFinite(a) ? a : 0),
                    );
                  }
                }
                if (totalArea > 0 && (suprafata == null || suprafata <= 0)) {
                  suprafata = totalArea;
                }
                if (!intravilan && intraVals.length > 0) {
                  intravilan = normalizeIntravilan(intraVals);
                }
                if (!categorie && catMap.size > 0) {
                  categorie = Array.from(catMap.entries())
                    .map(
                      ([k, a]) => `${k}:${a.toFixed(2).replace(/\.00$/, "")}`,
                    )
                    .join("; ");
                }
              }
            } catch {
              // parcel details fetch failed
            }
          }

          // 4. Fetch application data (solicitant, fallback folosință)
          if (immPk) {
            try {
              const apps = await client.fetchImmAppsByImmovable(
                immPk,
                workspaceId,
              );
              // Pick most recent application
              const chosen =
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                (apps ?? [])
                  .filter((a: any) => a?.dataCerere)
                  // eslint-disable-next-line @typescript-eslint/no-explicit-any
                  .sort(
                    (a: any, b: any) =>
                      (b.dataCerere ?? 0) - (a.dataCerere ?? 0),
                  )[0] ?? apps?.[0];

              if (chosen) {
                solicitant = String(chosen.solicitant ?? chosen.deponent ?? "");
                const appId = chosen.applicationId;
                if (appId) {
                  try {
                    const fol = await client.fetchParcelFolosinte(
                      workspaceId,
                      immPk,
                      appId,
                    );
                    intravilan = normalizeIntravilan(
                      // eslint-disable-next-line @typescript-eslint/no-explicit-any
                      (fol ?? []).map((f: any) => f?.intravilan ?? ""),
                    );
                    categorie = formatCategories(fol ?? []);
                    // Extract total area from folosinte as fallback
                    if (suprafata == null || suprafata <= 0) {
                      let totalArea = 0;
                      // eslint-disable-next-line @typescript-eslint/no-explicit-any
                      for (const f of (fol ?? []) as any[]) {
                        const a = Number(f?.suprafata ?? 0);
                        if (Number.isFinite(a)) totalArea += a;
                      }
                      if (totalArea > 0) suprafata = totalArea;
                    }
                  } catch {
                    // folosinta fetch failed
                  }
                }
              }
            } catch {
              // immApps fetch failed
            }
          }

          const allOwners = [...proprietariActuali, ...proprietariVechi];
          results.push({
            nrCad: String(item?.identifierDetails ?? cadNr),
            nrCF,
            nrCFVechi,
            nrTopo,
            intravilan,
            categorieFolosinta: categorie,
            adresa: addressText,
            proprietari: allOwners.join("; "),
            proprietariActuali: proprietariActuali.join("; "),
            proprietariVechi: proprietariVechi.join("; "),
            suprafata,
            solicitant,
            immovablePk: immPkStr,
          });
        }
      } catch {
        // Error for this particular cadNr — add placeholder
        results.push({
          nrCad: cadNr,
          nrCF: "",
          nrCFVechi: "",
          nrTopo: "",
          intravilan: "",
          categorieFolosinta: "",
          adresa: "",
          proprietari: "",
          proprietariActuali: "",
          proprietariVechi: "",
          suprafata: null,
          solicitant: "",
          immovablePk: "",
        });
      }
    }

    return NextResponse.json({
      results,
      total: results.length,
      source: "eterra-app",
    });
  } catch (error) {
    const message = error instanceof Error ? error.message : "Eroare server";
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
