import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import fs from "node:fs";

const ENV_PATH =
  process.env.ATTOM_ENV ?? "C:\\Users\\ahuja\\.attom\\env.txt";

function loadEnvFile(filePath: string): Record<string, string> {
  const raw = fs.readFileSync(filePath, "utf-8");
  const out: Record<string, string> = {};
  for (const line of raw.split(/\r?\n/)) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) continue;
    const eq = trimmed.indexOf("=");
    if (eq === -1) continue;
    out[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
  }
  return out;
}

function getConfig() {
  const env = loadEnvFile(ENV_PATH);
  if (!env.ATTOM_MW_BASE || !env.ATTOM_MW_TOKEN) {
    throw new Error(`Missing ATTOM_MW_BASE or ATTOM_MW_TOKEN in ${ENV_PATH}`);
  }
  return { base: env.ATTOM_MW_BASE.replace(/\/+$/, ""), token: env.ATTOM_MW_TOKEN };
}

async function callMiddleware(
  method: "GET" | "POST",
  pathAndQuery: string,
  body?: Record<string, unknown>
) {
  const { base, token } = getConfig();
  const res = await fetch(`${base}${pathAndQuery}`, {
    method,
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const text = await res.text();
  let json: unknown;
  try {
    json = JSON.parse(text);
  } catch {
    json = { raw: text };
  }
  if (!res.ok) {
    const hint: Record<number, string> = {
      401: "bad token",
      403: "missing scope",
      404: "address not found",
      422: "no job number",
      429: "rate limit or monthly ceiling reached",
      502: "ATTOM itself failed",
    };
    throw new Error(
      `ATTOM middleware HTTP ${res.status} (${hint[res.status] ?? "error"}): ${JSON.stringify(json)}`
    );
  }
  return json;
}

export function registerAttomTools(server: McpServer) {
  server.registerTool(
    "attom_resolve_property",
    {
      title: "Resolve an address to an ATTOM property ID",
      description:
        "POST /property/resolve — resolves an address, block/lot, APN or BBL to an ATTOM " +
        "property ID. Required first step before comparables/verification/assessment calls. " +
        "A job number is required on every ATTOM call (use the BDB order number, or an ad-hoc " +
        "tag like ADHOC-<short-desc> if no order exists yet).",
      inputSchema: {
        address: z.string().optional().describe("Full street address"),
        blockLot: z.string().optional().describe("Block/lot identifier"),
        apn: z.string().optional().describe("Assessor's Parcel Number"),
        bbl: z.string().optional().describe("NYC Borough-Block-Lot"),
        jobNumber: z.string().describe("BDB order number, or an ad-hoc tag if none exists yet"),
      },
    },
    async ({ address, blockLot, apn, bbl, jobNumber }) => {
      const result = await callMiddleware("POST", "/property/resolve", {
        address,
        block_lot: blockLot,
        apn,
        bbl,
        job_number: jobNumber,
      });
      return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
    }
  );

  server.registerTool(
    "attom_sales_comps",
    {
      title: "ATTOM sales comparables",
      description:
        "GET /property/{id}/comparables — sales comparables near a resolved ATTOM property. " +
        "ATTOM's licence and comp search are built for residential property; on commercial " +
        "subjects it returns few sales, so CoStar remains primary for commercial sales.",
      inputSchema: {
        attomId: z.string().describe("ATTOM property ID from attom_resolve_property"),
        jobNumber: z.string().describe("BDB order number, or an ad-hoc tag"),
        miles: z.number().min(0.1).max(50).optional().describe("Search radius, 0.1-50 (default 2)"),
        saleDateRangeMonths: z
          .number()
          .max(36)
          .optional()
          .describe("Sale date range in months, up to 36 (default 24)"),
        perPage: z.number().max(500).optional().describe("Results per page, up to 500"),
      },
    },
    async ({ attomId, jobNumber, miles, saleDateRangeMonths, perPage }) => {
      const params = new URLSearchParams();
      if (miles !== undefined) params.set("miles", String(miles));
      if (saleDateRangeMonths !== undefined)
        params.set("sale_date_range", String(saleDateRangeMonths));
      if (perPage !== undefined) params.set("per_page", String(perPage));
      params.set("job_number", jobNumber);
      const result = await callMiddleware(
        "GET",
        `/property/${encodeURIComponent(attomId)}/comparables?${params.toString()}`
      );
      return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
    }
  );

  server.registerTool(
    "attom_verification_bundle",
    {
      title: "ATTOM property verification bundle",
      description:
        "GET /property/{id}/verification-bundle — property detail (parcel, owner, building, " +
        "lot, zoning, assessment) for subject property verification.",
      inputSchema: {
        attomId: z.string().describe("ATTOM property ID from attom_resolve_property"),
        jobNumber: z.string().describe("BDB order number, or an ad-hoc tag"),
      },
    },
    async ({ attomId, jobNumber }) => {
      const params = new URLSearchParams({ job_number: jobNumber });
      const result = await callMiddleware(
        "GET",
        `/property/${encodeURIComponent(attomId)}/verification-bundle?${params.toString()}`
      );
      return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
    }
  );

  server.registerTool(
    "attom_assessment_history",
    {
      title: "ATTOM tax assessment history",
      description: "GET /property/{id}/assessment-history — tax assessment history for a property.",
      inputSchema: {
        attomId: z.string().describe("ATTOM property ID from attom_resolve_property"),
        jobNumber: z.string().describe("BDB order number, or an ad-hoc tag"),
      },
    },
    async ({ attomId, jobNumber }) => {
      const params = new URLSearchParams({ job_number: jobNumber });
      const result = await callMiddleware(
        "GET",
        `/property/${encodeURIComponent(attomId)}/assessment-history?${params.toString()}`
      );
      return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
    }
  );
}
