import { z } from "zod";

// ─── API Client ─────────────────────────────────────────────────

const API_URL = process.env.REONOMY_API_URL || "http://localhost:3847";

async function apiCall(method: string, path: string, body?: unknown): Promise<unknown> {
  const res = await fetch(`${API_URL}${path}`, {
    method,
    headers: { "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!res.ok) {
    const text = await res.text();
    throw new Error(`API ${method} ${path} failed (${res.status}): ${text}`);
  }
  return res.json();
}

// ─── Zod Schemas ────────────────────────────────────────────────

const BuildingFilterSchema = z.object({
  yearBuiltFrom: z.number().optional(),
  yearBuiltUntil: z.number().optional(),
  yearRenovatedFrom: z.number().optional(),
  yearRenovatedUntil: z.number().optional(),
  zoning: z.string().optional(),
  lotSizeSfMin: z.number().optional(),
  lotSizeSfMax: z.number().optional(),
  lotSizeAcresMin: z.number().optional(),
  lotSizeAcresMax: z.number().optional(),
  opportunityZone: z.boolean().optional(),
  totalUnitsMin: z.number().optional(),
  totalUnitsMax: z.number().optional(),
  buildingAreaMin: z.number().optional(),
  buildingAreaMax: z.number().optional(),
}).optional();

const OwnerFilterSchema = z.object({
  nameOrCompany: z.string().optional(),
  ownerType: z.enum(["Company", "Person"]).optional(),
  includesPhone: z.boolean().optional(),
  includesEmail: z.boolean().optional(),
  includesMailingAddress: z.boolean().optional(),
  portfolioMin: z.number().optional(),
  portfolioMax: z.number().optional(),
  ownerOccupied: z.boolean().optional(),
  inStateOwner: z.boolean().optional(),
  portfolioValueMin: z.number().optional(),
  portfolioValueMax: z.number().optional(),
  reportedOwner: z.string().optional(),
  mailingAddress: z.string().optional(),
}).optional();

const OccupantsFilterSchema = z.object({
  name: z.string().optional(),
  naicsSic: z.string().optional(),
  website: z.string().optional(),
}).optional();

const SalesFilterSchema = z.object({
  dateRange: z.string().optional(),
  multiParcel: z.boolean().optional(),
  priceMin: z.number().optional(),
  priceMax: z.number().optional(),
  pricePerSfMin: z.number().optional(),
  pricePerSfMax: z.number().optional(),
  likelyToSell: z.boolean().optional(),
}).optional();

const DebtFilterSchema = z.object({
  amountMin: z.number().optional(),
  amountMax: z.number().optional(),
  originationFrom: z.string().optional(),
  originationUntil: z.string().optional(),
  maturityFrom: z.string().optional(),
  maturityUntil: z.string().optional(),
  lenderName: z.string().optional(),
  cmbsLoan: z.boolean().optional(),
}).optional();

const DistressedFilterSchema = z.object({
  auctionDateFrom: z.string().optional(),
  auctionDateUntil: z.string().optional(),
  preForeclosureCategory: z.string().optional(),
  cmbsWatchlist: z.boolean().optional(),
}).optional();

const SearchConfigSchema = z.object({
  location: z.string().describe("Location to search, e.g. 'Miami-Dade, FL'"),
  propertyTypes: z.array(z.string()).optional().describe("Property types to filter, e.g. ['Multifamily', 'Office']"),
  building: BuildingFilterSchema,
  owner: OwnerFilterSchema,
  occupants: OccupantsFilterSchema,
  sales: SalesFilterSchema,
  debt: DebtFilterSchema,
  distressed: DistressedFilterSchema,
});

const OutputConfigSchema = z.object({
  propertyInfo: z.array(z.enum(["address", "type", "units", "sqft", "yearBuilt", "lotSize", "zoning", "opportunityZone", "apn", "legal"])).optional(),
  ownerInfo: z.array(z.enum(["name", "company", "portfolioSize", "portfolioValue", "ownerType"])).optional(),
  contactInfo: z.array(z.enum(["phones", "emails"])).optional(),
  salesInfo: z.array(z.enum(["lastSaleDate", "lastSalePrice", "buyer", "seller", "deedType"])).optional(),
  debtInfo: z.array(z.enum(["lender", "loanType", "mortgageAmount", "maturityDate", "interestType"])).optional(),
  taxInfo: z.array(z.enum(["assessedValue", "taxAmount"])).optional(),
});

// ─── Tool Definitions ───────────────────────────────────────────

export interface ToolDef {
  name: string;
  description: string;
  inputSchema: z.ZodType;
  meta?: Record<string, unknown>;
  handler: (args: unknown) => Promise<ToolResult>;
}

export interface ToolResult {
  content: Array<{ type: "text"; text: string }>;
  _meta?: Record<string, unknown>;
}

export const tools: ToolDef[] = [
  // ── reonomy_search ──
  {
    name: "reonomy_search",
    description:
      "Configure and execute a Reonomy property search with location, property type, building, owner, sales, debt, and distressed filters. Returns a search ID with total property count.",
    inputSchema: SearchConfigSchema,
    handler: async (args) => {
      const config = SearchConfigSchema.parse(args);
      const result = (await apiCall("POST", "/api/scrape", {
        searchConfig: config,
      })) as { jobId: string; searchId: string; totalProperties: number };

      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(
              {
                searchId: result.searchId || result.jobId,
                totalProperties: result.totalProperties,
                jobId: result.jobId,
                message: `Found ${result.totalProperties} properties matching your search in ${config.location}`,
              },
              null,
              2
            ),
          },
        ],
      };
    },
  },

  // ── reonomy_scrape ──
  {
    name: "reonomy_scrape",
    description:
      "Start scraping property data from a previous search. Specify which output fields to extract and max properties to scrape.",
    inputSchema: z.object({
      searchId: z.string().describe("Search ID from reonomy_search"),
      outputConfig: OutputConfigSchema.optional().describe("Fields to extract per property"),
      maxProperties: z.number().optional().default(50).describe("Max properties to scrape (default 50)"),
    }),
    meta: { "ui.resourceUri": "reonomy://app/results" },
    handler: async (args) => {
      const { searchId, outputConfig, maxProperties } = z
        .object({
          searchId: z.string(),
          outputConfig: OutputConfigSchema.optional(),
          maxProperties: z.number().optional().default(50),
        })
        .parse(args);

      const result = (await apiCall("POST", "/api/scrape", {
        searchId,
        outputConfig: outputConfig || {
          propertyInfo: ["address", "type", "units", "sqft", "yearBuilt"],
          ownerInfo: ["name", "company"],
          contactInfo: ["phones", "emails"],
        },
        maxProperties,
      })) as { jobId: string; status: string; estimatedTime?: number };

      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(
              {
                jobId: result.jobId,
                status: result.status,
                estimatedTime: result.estimatedTime,
                message: `Scraping started for ${maxProperties} properties`,
              },
              null,
              2
            ),
          },
        ],
        _meta: { "ui.resourceUri": "reonomy://app/results" },
      };
    },
  },

  // ── reonomy_get_results ──
  {
    name: "reonomy_get_results",
    description:
      "Get scraped property results for a job. Supports pagination with offset/limit.",
    inputSchema: z.object({
      jobId: z.string().describe("Job ID from reonomy_scrape"),
      offset: z.number().optional().default(0).describe("Pagination offset"),
      limit: z.number().optional().default(25).describe("Results per page (max 100)"),
    }),
    meta: { "ui.resourceUri": "reonomy://app/results" },
    handler: async (args) => {
      const { jobId, offset, limit } = z
        .object({
          jobId: z.string(),
          offset: z.number().optional().default(0),
          limit: z.number().optional().default(25),
        })
        .parse(args);

      const result = (await apiCall(
        "GET",
        `/api/scrape/${jobId}/results?offset=${offset}&limit=${limit}`
      )) as {
        jobId: string;
        status: string;
        totalResults: number;
        results: unknown[];
      };

      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(
              {
                jobId: result.jobId,
                status: result.status,
                totalResults: result.totalResults,
                showing: { offset, limit, count: result.results.length },
                results: result.results,
              },
              null,
              2
            ),
          },
        ],
        _meta: { "ui.resourceUri": "reonomy://app/results" },
      };
    },
  },

  // ── reonomy_get_filters ──
  {
    name: "reonomy_get_filters",
    description:
      "Get all available Reonomy filter options organized by category (property types, building, owner, sales, debt, distressed). Use this to understand what search parameters are available before running a search.",
    inputSchema: z.object({}),
    meta: { "ui.resourceUri": "reonomy://app/search" },
    handler: async () => {
      let filters: unknown;
      try {
        filters = await apiCall("GET", "/api/filters");
      } catch {
        // Fallback: return the built-in filter catalog
        filters = getBuiltInFilters();
      }

      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(filters, null, 2),
          },
        ],
        _meta: { "ui.resourceUri": "reonomy://app/search" },
      };
    },
  },

  // ── reonomy_export ──
  {
    name: "reonomy_export",
    description:
      "Export scraped results as CSV or JSON. Returns download URL or inline data.",
    inputSchema: z.object({
      jobId: z.string().describe("Job ID to export results for"),
      format: z.enum(["csv", "json"]).describe("Export format"),
    }),
    handler: async (args) => {
      const { jobId, format } = z
        .object({
          jobId: z.string(),
          format: z.enum(["csv", "json"]),
        })
        .parse(args);

      const result = (await apiCall(
        "GET",
        `/api/exports/${jobId}?format=${format}`
      )) as { downloadUrl?: string; data?: unknown; filename?: string };

      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(
              {
                format,
                downloadUrl: result.downloadUrl,
                filename: result.filename,
                message: result.downloadUrl
                  ? `Export ready: ${result.downloadUrl}`
                  : `Export data included inline (${format})`,
                ...(result.data && !result.downloadUrl ? { data: result.data } : {}),
              },
              null,
              2
            ),
          },
        ],
      };
    },
  },
];

// ─── Built-in Filter Catalog ────────────────────────────────────

function getBuiltInFilters() {
  return {
    propertyTypes: [
      "Multifamily",
      "Multi Family (General)",
      "Office",
      "Retail",
      "Industrial",
      "Hospitality",
      "Land",
      "Mixed Use",
      "Special Purpose",
      "Healthcare",
      "Self Storage",
      "Senior Living",
      "Student Housing",
      "Manufactured Housing",
    ],
    building: {
      yearBuilt: { description: "Filter by year built range", type: "range", min: 1800, max: 2026 },
      yearRenovated: { description: "Filter by year renovated range", type: "range", min: 1900, max: 2026 },
      zoning: { description: "Zoning classification", type: "text" },
      lotSize: { description: "Lot size in sq ft or acres", type: "range" },
      opportunityZone: { description: "In an Opportunity Zone", type: "boolean" },
      totalUnits: { description: "Number of units", type: "range" },
      buildingArea: { description: "Building area in sq ft", type: "range" },
    },
    owner: {
      nameOrCompany: { description: "Owner name or company search", type: "text" },
      ownerType: { description: "Company or Person", type: "enum", values: ["Company", "Person"] },
      includesPhone: { description: "Owner has phone number on file", type: "boolean" },
      includesEmail: { description: "Owner has email on file", type: "boolean" },
      includesMailingAddress: { description: "Owner has mailing address on file", type: "boolean" },
      portfolio: { description: "Portfolio size range", type: "range" },
      ownerOccupied: { description: "Owner-occupied property", type: "boolean" },
      inStateOwner: { description: "Owner is in-state", type: "boolean" },
      portfolioValue: { description: "Portfolio value range", type: "range" },
    },
    occupants: {
      name: { description: "Occupant/tenant name", type: "text" },
      naicsSic: { description: "NAICS/SIC industry code", type: "text" },
      website: { description: "Business website", type: "text" },
    },
    sales: {
      dateRange: { description: "Sale date range", type: "dateRange" },
      multiParcel: { description: "Multi-parcel transactions", type: "boolean" },
      price: { description: "Sale price range", type: "range" },
      pricePerSf: { description: "Price per sq ft range", type: "range" },
      likelyToSell: { description: "Property likely to sell", type: "boolean" },
    },
    debt: {
      amount: { description: "Mortgage amount range", type: "range" },
      origination: { description: "Loan origination date range", type: "dateRange" },
      maturity: { description: "Loan maturity date range", type: "dateRange" },
      lenderName: { description: "Lender name search", type: "text" },
      cmbsLoan: { description: "CMBS loan", type: "boolean" },
    },
    distressed: {
      auctionDate: { description: "Foreclosure auction date range", type: "dateRange" },
      preForeclosureCategory: { description: "Pre-foreclosure category", type: "text" },
      cmbsWatchlist: { description: "On CMBS watchlist", type: "boolean" },
    },
    outputFields: {
      propertyInfo: ["address", "type", "units", "sqft", "yearBuilt", "lotSize", "zoning", "opportunityZone", "apn", "legal"],
      ownerInfo: ["name", "company", "portfolioSize", "portfolioValue", "ownerType"],
      contactInfo: ["phones", "emails"],
      salesInfo: ["lastSaleDate", "lastSalePrice", "buyer", "seller", "deedType"],
      debtInfo: ["lender", "loanType", "mortgageAmount", "maturityDate", "interestType"],
      taxInfo: ["assessedValue", "taxAmount"],
    },
  };
}
