import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import assert from "node:assert/strict";

import { assessProperty } from "../src/assessment.js";
import type { PublicRecordsResolution } from "../src/public-records.js";

const samplePublicRecords: PublicRecordsResolution = {
  requestedAddress: "4141 Whiteley Dr, Corpus Christi, TX 78418",
  matchedAddress: "4141 WHITELEY DR, CORPUS CHRISTI, TX, 78418",
  latitude: 27.6138,
  longitude: -97.3024,
  geoid: "483550031013005",
  state: {
    name: "Texas",
    code: "TX",
    fips: "48"
  },
  county: {
    name: "Nueces County",
    fips: "355",
    geoid: "48355"
  },
  officialLinks: {
    censusGeocoder: "https://geocoding.geo.census.gov/example",
    texasCountyDirectory:
      "https://comptroller.texas.gov/taxes/property-tax/county-directory/nueces.php",
    texasPropertyTaxPortal: "https://texas.gov/PropertyTaxes"
  },
  appraisalDistrict: {
    "Chief Appraiser": "Debra Morin, Interim",
    Website: "http://www.ncadistrict.com/",
    directoryPage:
      "https://comptroller.texas.gov/taxes/property-tax/county-directory/nueces.php"
  },
  taxAssessorCollector: {
    "Tax Assessor-Collector": "Kevin Kieschnick",
    Website: "http://www.nuecesco.com"
  },
  lookupRecommendations: [
    "Start from the official public-record jurisdiction instead of a listing-site geo ID."
  ],
  sourceIdentifierHints: {
    parcelId: "14069438"
  },
  propertyDetails: {
    source: "nueces-esearch",
    sourceUrl: "https://esearch.nuecescad.net/property/view/14069438?year=2026",
    propertyId: "14069438",
    ownerName: "Fiorini Family Trust",
    situsAddress: "4141 WHITELEY DR, CORPUS CHRISTI, TX, 78418",
    legalDescription: "LOT 4 BLOCK 3 EXAMPLE SUBDIVISION",
    landValue: 42000,
    improvementValue: 99000,
    assessedTotalValue: 141000,
    exemptions: ["Homestead"],
    notes: [
      "Official CAD property detail page exposed owner, value, and exemption data."
    ]
  }
};

test("assessProperty asks for assessment purpose before building a decision-grade assessment", async () => {
  const result = await assessProperty(
    {
      address: "4141 Whiteley Dr, Corpus Christi, TX 78418"
    }
  );

  assert.equal(result.ok, true);
  assert.equal(result.needsAssessmentPurpose, true);
  assert.equal(result.needsRecipientEmails, false);
  assert.equal(result.outputPath, null);
  assert.match(result.message, /assessment purpose/i);
  assert.equal(result.reportPayload, null);
});

test("assessProperty auto-discovers listing sources, runs Zillow photos first, and does not ask for email during analysis-only runs", async () => {
  const result = await assessProperty(
    {
      address: "4141 Whiteley Dr, Corpus Christi, TX 78418",
      assessmentPurpose: "investment property",
      listingGeoId: "233290",
      parcelId: "14069438"
    },
    {
      resolvePublicRecordsFn: async () => samplePublicRecords,
      discoverListingSourcesFn: async () => ({
        attempts: [
          "Zillow discovery located a property page from the address.",
          "HAR discovery located a property page from the address."
        ],
        zillowUrl:
          "https://www.zillow.com/homedetails/4141-Whiteley-Dr-Corpus-Christi-TX-78418/2103723704_zpid/",
        harUrl:
          "https://www.har.com/homedetail/4141-whiteley-dr-corpus-christi-tx-78418/14069438"
      }),
      extractPhotoDataFn: async (source, url) => ({
        source,
        requestedUrl: url,
        finalUrl: url,
        expectedPhotoCount: 29,
        complete: true,
        photoCount: 29,
        imageUrls: ["https://photos.example/1.jpg", "https://photos.example/2.jpg"],
        notes: [`${source} extractor succeeded.`]
      })
    }
  );

  assert.equal(result.ok, true);
  assert.equal(result.needsAssessmentPurpose, false);
  assert.equal(result.needsRecipientEmails, false);
  assert.equal(result.outputPath, null);
  assert.doesNotMatch(result.message, /target email/i);
  assert.match(result.message, /ready to render|recipient email is only needed when you want the pdf/i);
  assert.equal(result.reportPayload?.subjectProperty?.address, samplePublicRecords.matchedAddress);
  assert.equal(result.reportPayload?.publicRecords?.jurisdiction, "Nueces County Appraisal District");
  assert.equal(result.reportPayload?.publicRecords?.accountNumber, "14069438");
  assert.equal(result.reportPayload?.publicRecords?.ownerName, "Fiorini Family Trust");
  assert.equal(result.reportPayload?.publicRecords?.landValue, 42000);
  assert.equal(result.reportPayload?.publicRecords?.improvementValue, 99000);
  assert.equal(result.reportPayload?.publicRecords?.assessedTotalValue, 141000);
  assert.deepEqual(result.reportPayload?.publicRecords?.exemptions, ["Homestead"]);
  assert.match(
    String(result.reportPayload?.snapshot?.join(" ")),
    /141,000|141000|assessed/i
  );
  assert.match(
    String(result.reportPayload?.risksAndDiligence?.join(" ")),
    /official cad property detail page exposed owner, value, and exemption data/i
  );
  assert.equal(result.reportPayload?.photoReview?.status, "completed");
  assert.equal(result.reportPayload?.photoReview?.source, "zillow");
  assert.equal(result.reportPayload?.photoReview?.photoCount, 29);
  assert.match(
    String(result.reportPayload?.verdict?.offerGuidance),
    /investment property/i
  );
  assert.match(
    String(result.reportPayload?.carryView?.[0]),
    /income property/i
  );
  assert.deepEqual(result.reportPayload?.recipientEmails, []);
});

test("assessProperty uses parcel/APN hints extracted from Zillow before CAD lookup when parcel ID was not provided", async () => {
  const seenPublicRecordOptions: Array<Record<string, unknown>> = [];

  const result = await assessProperty(
    {
      address: "6702 Everhart Rd APT T106, Corpus Christi, TX 78413",
      assessmentPurpose: "college housing for daughter attending TAMU-CC"
    },
    {
      resolvePublicRecordsFn: async (_address, options) => {
        seenPublicRecordOptions.push({ ...options });
        return samplePublicRecords;
      },
      discoverListingSourcesFn: async () => ({
        attempts: ["Zillow discovery located a property page from the address."],
        zillowUrl:
          "https://www.zillow.com/homedetails/6702-Everhart-Rd-APT-T106-Corpus-Christi-TX-78413/2067445642_zpid/",
        harUrl: null
      }),
      extractZillowIdentifierHintsFn: async () => ({
        parcelId: "1234567890",
        notes: ["Zillow listing exposed parcel/APN number 1234567890."]
      }),
      extractPhotoDataFn: async (source, url) => ({
        source,
        requestedUrl: url,
        finalUrl: url,
        expectedPhotoCount: 29,
        complete: true,
        photoCount: 29,
        imageUrls: ["https://photos.example/1.jpg"],
        notes: [`${source} extractor succeeded.`]
      })
    }
  );

  assert.equal(result.ok, true);
  assert.equal(seenPublicRecordOptions.length, 1);
  assert.equal(seenPublicRecordOptions[0]?.parcelId, "1234567890");
});

test("assessProperty asks for recipient email only when PDF render is explicitly requested", async () => {
  const result = await assessProperty(
    {
      address: "4141 Whiteley Dr, Corpus Christi, TX 78418",
      assessmentPurpose: "investment property",
      output: path.join(os.tmpdir(), `property-assess-missing-email-${Date.now()}.pdf`)
    },
    {
      resolvePublicRecordsFn: async () => samplePublicRecords,
      discoverListingSourcesFn: async () => ({
        attempts: ["Zillow discovery located a property page from the address."],
        zillowUrl:
          "https://www.zillow.com/homedetails/4141-Whiteley-Dr-Corpus-Christi-TX-78418/2103723704_zpid/",
        harUrl: null
      }),
      extractPhotoDataFn: async (source, url) => ({
        source,
        requestedUrl: url,
        finalUrl: url,
        expectedPhotoCount: 29,
        complete: true,
        photoCount: 29,
        imageUrls: ["https://photos.example/1.jpg"],
        notes: [`${source} extractor succeeded.`]
      })
    }
  );

  assert.equal(result.ok, true);
  assert.equal(result.needsRecipientEmails, true);
  assert.equal(result.outputPath, null);
  assert.match(result.message, /target email/i);
});

test("assessProperty falls back to HAR when Zillow photo extraction fails", async () => {
  const result = await assessProperty(
    {
      address: "4141 Whiteley Dr, Corpus Christi, TX 78418",
      assessmentPurpose: "vacation home"
    },
    {
      resolvePublicRecordsFn: async () => samplePublicRecords,
      discoverListingSourcesFn: async () => ({
        attempts: ["Address-based discovery found Zillow and HAR candidates."],
        zillowUrl:
          "https://www.zillow.com/homedetails/4141-Whiteley-Dr-Corpus-Christi-TX-78418/2103723704_zpid/",
        harUrl:
          "https://www.har.com/homedetail/4141-whiteley-dr-corpus-christi-tx-78418/14069438"
      }),
      extractPhotoDataFn: async (source, url) => {
        if (source === "zillow") {
          throw new Error(`zillow failed for ${url}`);
        }
        return {
          source,
          requestedUrl: url,
          finalUrl: url,
          expectedPhotoCount: 29,
          complete: true,
          photoCount: 29,
          imageUrls: ["https://photos.har.example/1.jpg"],
          notes: ["HAR extractor succeeded after Zillow failed."]
        };
      }
    }
  );

  assert.equal(result.ok, true);
  assert.equal(result.reportPayload?.photoReview?.status, "completed");
  assert.equal(result.reportPayload?.photoReview?.source, "har");
  assert.match(
    String(result.reportPayload?.photoReview?.attempts?.join(" ")),
    /zillow/i
  );
  assert.match(
    String(result.reportPayload?.verdict?.offerGuidance),
    /vacation home/i
  );
});

test("assessProperty does not render a PDF from a preliminary helper payload even when recipient email is present", async () => {
  const outputPath = path.join(os.tmpdir(), `property-assess-command-${Date.now()}.pdf`);
  let renderCalls = 0;
  const result = await assessProperty(
    {
      address: "4141 Whiteley Dr, Corpus Christi, TX 78418",
      assessmentPurpose: "rental for my daughter in college",
      recipientEmails: ["buyer@example.com"],
      output: outputPath
    },
    {
      resolvePublicRecordsFn: async () => samplePublicRecords,
      discoverListingSourcesFn: async () => ({
        attempts: ["No listing sources discovered from the address."],
        zillowUrl: null,
        harUrl: null
      }),
      renderReportPdfFn: async () => {
        renderCalls += 1;
        return outputPath;
      }
    }
  );

  assert.equal(result.ok, true);
  assert.equal(result.needsAssessmentPurpose, false);
  assert.equal(result.needsRecipientEmails, false);
  assert.equal(renderCalls, 0);
  assert.equal(result.pdfReady, false);
  assert.equal(result.outputPath, null);
  assert.match(result.message, /preliminary|decision-grade|cannot render/i);
});

test("assessProperty prioritizes student housing guidance over investment fallback keywords", async () => {
  const result = await assessProperty(
    {
      address: "1011 Ennis Joslin Rd APT 235, Corpus Christi, TX 78412",
      assessmentPurpose:
        "college housing for daughter attending TAMU-CC; prioritize proximity, safety/livability, and resale/rental fallback"
    },
    {
      resolvePublicRecordsFn: async () => samplePublicRecords,
      discoverListingSourcesFn: async () => ({
        attempts: ["Zillow discovery located a property page from the address."],
        zillowUrl:
          "https://www.zillow.com/homedetails/1011-Ennis-Joslin-Rd-APT-235-Corpus-Christi-TX-78412/28848927_zpid/",
        harUrl: null
      }),
      extractPhotoDataFn: async (source, url) => ({
        source,
        requestedUrl: url,
        finalUrl: url,
        expectedPhotoCount: 20,
        complete: true,
        photoCount: 20,
        imageUrls: ["https://photos.example/1.jpg"],
        notes: [`${source} extractor succeeded.`]
      })
    }
  );

  assert.match(
    String(result.reportPayload?.verdict?.offerGuidance),
    /daughter|student|practicality|safety/i
  );
  assert.doesNotMatch(
    String(result.reportPayload?.verdict?.offerGuidance),
    /income property|investment property/i
  );
  assert.match(
    String(result.reportPayload?.carryView?.[0]),
    /parent-risk|upkeep burden|renting/i
  );
});
