import { NextResponse } from "next/server";
import fs from "fs";
import path from "path";
import { GoogleGenerativeAI } from "@google/generative-ai";

// Helper to map Google's category or types to our 5 baseline categories.
function mapTypeToBaseline(primaryType: string, types: string[]): string {
  const allTypes = [primaryType, ...types].filter(Boolean);
  const text = allTypes.join(" ").toLowerCase();

  if (
    text.includes("health") ||
    text.includes("salon") ||
    text.includes("hair") ||
    text.includes("beauty") ||
    text.includes("spa") ||
    text.includes("dentist") ||
    text.includes("clinic") ||
    text.includes("doctor") ||
    text.includes("tutor") ||
    text.includes("consultant") ||
    text.includes("hygienist")
  ) {
    return "appointments"; // Booking/scheduling
  }
  if (
    text.includes("plumber") ||
    text.includes("hvac") ||
    text.includes("electrician") ||
    text.includes("contractor") ||
    text.includes("clean") ||
    text.includes("landscape") ||
    text.includes("lawn") ||
    text.includes("builder") ||
    text.includes("repair")
  ) {
    return "service"; // Trades & FSM
  }
  if (
    text.includes("bakery") ||
    text.includes("cafe") ||
    text.includes("café") ||
    text.includes("food") ||
    text.includes("restaurant") ||
    text.includes("bar") ||
    text.includes("catering")
  ) {
    return "food"; // Food & Drink
  }
  if (
    text.includes("boutique") ||
    text.includes("store") ||
    text.includes("retail") ||
    text.includes("shop") ||
    text.includes("market")
  ) {
    return "retail"; // Retail POS
  }
  if (
    text.includes("venue") ||
    text.includes("event") ||
    text.includes("planner") ||
    text.includes("rental")
  ) {
    return "events"; // Events
  }
  return "other"; // Default
}

// Helper to scrape HTML <title> from a given domain.
async function fetchWebsiteTitle(urlStr: string): Promise<string | null> {
  let url = urlStr.trim();
  if (!/^https?:\/\//i.test(url)) {
    url = "https://" + url; // try https first
  }
  try {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 1800);
    const res = await fetch(url, { signal: controller.signal });
    clearTimeout(timeout);
    if (res.ok) {
      const html = await res.text();
      const match = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
      if (match && match[1]) {
        return match[1]
          .trim()
          .replace(/\s+/g, " ")
          .replace(/&ndash;/g, "–")
          .replace(/&mdash;/g, "—")
          .replace(/&amp;/g, "&");
      }
    }
  } catch (err) {
    // silently fallback
  }
  return null;
}

// POST /api/agency/places/search
// Queries DataForSEO's Business Listings Search API (Most Complete GMB scraper)
// to fetch storefronts and Service Area Businesses (SABs) with exact additional_categories.
// Fallback: Google Places API if DataForSEO fails.
export async function POST(request: Request) {
  try {
    const { name, city } = await request.json();
    if (!name || !city) {
      return NextResponse.json(
        { error: "name and city required" },
        { status: 400 },
      );
    }

    const cityName = city.name || "";
    const cityRegion = city.region || "";

    // 1. Query DataForSEO Business Listings Database
    const DFS_LOGIN = "mark@getacquired.com";
    const DFS_PASSWORD = "c9893141f2ee1d50";
    const DFS_AUTH =
      "Basic " + Buffer.from(DFS_LOGIN + ":" + DFS_PASSWORD).toString("base64");

    const dfsUrl =
      "https://api.dataforseo.com/v3/business_data/business_listings/search/live";

    // Build flexible name matching filter (handle domains or text names)
    const isUrl = /\.[a-z]{2,}/i.test(name) || /^https?:\/\//i.test(name);
    const cleanSearchName = name
      .replace(/^https?:\/\//i, "")
      .replace(/^www\./i, "")
      .split("/")[0];

    const bodyObj: {
      limit: number;
      location_coordinate?: string;
      filters?: Array<string | string[] | Array<string | string[]>>;
    } = {
      limit: 3,
    };

    // 1. Establish the filter array. In DataForSEO's Listings API, ALL field queries
    // (including title, domain, and city) MUST be passed inside the filters array,
    // NOT as top-level JSON body fields.
    const filters: Array<string | string[] | Array<string | string[]>> = [];

    if (isUrl) {
      // Use 'like' with % wildcard so we match both www.domain.com and domain.com exactly
      filters.push(["domain", "like", `%${cleanSearchName}%`]);
    } else {
      filters.push(["title", "like", `%${name}%`]);
    }

    // Radial geospatial search (geofencing) is the ONLY way to fetch Service Area Businesses (SABs)
    // because GMB stores their city/address text fields as NULL.
    if (typeof city.lat === "number" && typeof city.lng === "number") {
      bodyObj.location_coordinate = `${city.lat},${city.lng},50`; // 50km radius geofence
    } else {
      // Fallback city text filter if no coordinates (e.g. offline dev seeds)
      filters.push("and", ["address_info.city", "=", cityName]);
    }

    bodyObj.filters = filters;

    const payload = [bodyObj];

    let dfMatches: Array<{
      title?: string;
      address?: string;
      category?: string;
      category_ids?: string[];
      additional_categories?: string[];
      cid?: string;
      feature_id?: string;
    }> = [];
    try {
      const response = await fetch(dfsUrl, {
        method: "POST",
        headers: {
          Authorization: DFS_AUTH,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(payload),
      });

      if (response.ok) {
        const data = await response.json();
        const rawResult = data.tasks?.[0]?.result?.[0];
        dfMatches = Array.isArray(rawResult?.items) ? rawResult.items : [];
      }

      // If no matches found with the full title, try a shorter 2-word title query (self-healing fallback!)
      if (dfMatches.length === 0 && !isUrl) {
        const words = name.trim().split(/\s+/);
        if (words.length > 2) {
          const shortName = words.slice(0, 2).join(" ");
          const shortPayload = [
            {
              filters: [
                ["address_info.city", "=", cityName],
                "and",
                ["title", "like", `%${shortName}%`],
              ],
              limit: 3,
            },
          ];
          const shortResponse = await fetch(dfsUrl, {
            method: "POST",
            headers: {
              Authorization: DFS_AUTH,
              "Content-Type": "application/json",
            },
            body: JSON.stringify(shortPayload),
          });
          if (shortResponse.ok) {
            const shortData = await shortResponse.json();
            const shortRaw = shortData.tasks?.[0]?.result?.[0];
            dfMatches = Array.isArray(shortRaw?.items) ? shortRaw.items : [];
          }
        }
      }
    } catch (dfsErr) {
      console.error("DataForSEO query failed, falling back to Google:", dfsErr);
    }

    // 2. Load smb_to_software_mapping_final.json
    const mappingPath = path.join(
      process.cwd(),
      "..",
      "market_data_assets",
      "smb_to_software_mapping_final.json",
    );
    const mappingDict: Record<
      string,
      { displayName: string; softwareNeeds: string[] }
    > = {};
    try {
      if (fs.existsSync(mappingPath)) {
        const raw = fs.readFileSync(mappingPath, "utf-8");
        const list = JSON.parse(raw);
        if (Array.isArray(list)) {
          list.forEach(
            (entry: {
              gcid: string;
              displayName: string;
              softwareNeeds: string[];
            }) => {
              if (entry.gcid) mappingDict[entry.gcid] = entry;
            },
          );
        }
      }
    } catch (e) {
      console.error("Failed to load software mapping JSON:", e);
    }

    // 3. Process matches (prefer DataForSEO, fallback to Google Places)
    let finalMatches = [];

    if (dfMatches && dfMatches.length > 0) {
      // DataForSEO success! Extracted GMB details (storefronts + SABs!)
      finalMatches = dfMatches.map((item) => {
        const title = item.title || "";
        const vicinity = item.address || `${cityName}, ${cityRegion}, Canada`;
        const primaryCategory = item.category || "";
        const additionalCategories: string[] = Array.isArray(
          item.additional_categories,
        )
          ? item.additional_categories
          : [];

        // Check if GMB primary category is directly in our 4,006 mapping file
        let resolvedGcid = `gcid:${item.category_ids?.[0] || "local_business"}`;
        let matchedRecord = mappingDict[resolvedGcid];

        // Try mapping additional categories if primary not matched
        if (!matchedRecord && additionalCategories.length > 0) {
          for (const extra of additionalCategories) {
            const extraGcid = `gcid:${extra.toLowerCase().replace(/[^a-z0-9]+/g, "_")}`;
            if (mappingDict[extraGcid]) {
              resolvedGcid = extraGcid;
              matchedRecord = mappingDict[extraGcid];
              break;
            }
          }
        }

        const typeLabel =
          matchedRecord?.displayName || primaryCategory || "Local Business";
        const bizType = mapTypeToBaseline(
          primaryCategory,
          additionalCategories,
        );
        const presetTools = matchedRecord?.softwareNeeds || undefined;

        // Custom builder description
        const cleanTools = (presetTools || [])
          .slice(0, 3)
          .map((t) => t.replace(/\s*Software$/i, ""))
          .join(", ");
        const description = cleanTools
          ? `Presets: ${cleanTools}. Built as a custom unified workspace for your business.`
          : "Custom local business tools. Presets: CRM, Invoicing, and Scheduling.";

        // Resolve any alternative categories found in GMB additional_categories
        const alternativeCategories: Array<{
          gcid: string;
          typeLabel: string;
          presetTools: string[];
          description: string;
        }> = [];
        additionalCategories.forEach((extra: string) => {
          const extraGcid = `gcid:${extra.toLowerCase().replace(/[^a-z0-9]+/g, "_")}`;
          const extraRecord = mappingDict[extraGcid];
          if (extraRecord && extraGcid !== resolvedGcid) {
            const extraTools = extraRecord.softwareNeeds || [];
            const extraClean = extraTools
              .slice(0, 3)
              .map((t) => t.replace(/\s*Software$/i, ""))
              .join(", ");
            alternativeCategories.push({
              gcid: extraGcid,
              typeLabel: extraRecord.displayName,
              presetTools: extraTools,
              description: extraClean
                ? `Presets: ${extraClean}. Built as a custom unified workspace for your business.`
                : "Custom local business tools. Presets: CRM, Invoicing, and Scheduling.",
            });
          }
        });

        return {
          placeId: item.cid || item.feature_id || "dfs-opt",
          name: title,
          vicinity,
          primaryType: primaryCategory,
          typeLabel,
          bizType,
          gcid: resolvedGcid,
          description,
          presetTools,
          alternativeCategories:
            alternativeCategories.length > 0
              ? alternativeCategories
              : undefined,
        };
      });
    } else {
      // Fallback: Google Places API Text Search (New) + Gemini AI Resolver
      const apiKey =
        process.env.GOOGLE_PLACES_API_KEY || process.env.GOOGLE_API_KEY;
      if (!apiKey) {
        return NextResponse.json(
          { error: "GCP API Key missing" },
          { status: 500 },
        );
      }

      const scrapedTitle = isUrl ? await fetchWebsiteTitle(name) : null;
      const searchQuery = scrapedTitle
        ? `${scrapedTitle} ${cityName} ${cityRegion}`
        : `${name} ${cityName} ${cityRegion}`;

      const searchUrl = "https://places.googleapis.com/v1/places:searchText";
      const response = await fetch(searchUrl, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Goog-Api-Key": apiKey,
          "X-Goog-FieldMask":
            "places.id,places.displayName,places.formattedAddress,places.primaryType,places.types,places.websiteUri",
        },
        body: JSON.stringify({
          textQuery: searchQuery,
          maxResultCount: 3,
        }),
      });

      if (response.ok) {
        const data = await response.json();
        const places = data.places || [];

        const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || "");
        const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });

        finalMatches = await Promise.all(
          places.map(
            async (p: {
              id: string;
              displayName?: { text: string };
              formattedAddress?: string;
              primaryType?: string;
              types?: string[];
            }) => {
              const displayName = p.displayName?.text || "";
              const allTypes = [p.primaryType, ...(p.types || [])].filter(
                Boolean,
              );
              const vicinity = p.formattedAddress || "";
              const primaryType = p.primaryType || "local_business";

              let resolvedGcid = `gcid:${primaryType}`;
              let bizType = mapTypeToBaseline(primaryType, p.types || []);

              try {
                const prompt = `
              Identify the single most accurate, specific business type/industry for this local business:
              - Business Name: "${displayName}"
              - Entered Query: "${name}"
              - Google Places Category: "${primaryType}"
              - Google Other Tags: [${allTypes.join(", ")}]
              - Website Title Context: "${scrapedTitle || ""}"

              Identify their single core business type. Choose one of our standard industry terms if possible (e.g. "Dental hygienist", "Dentist", "Plumber", "HVAC contractor", "Hair salon", "Beauty salon", "Restaurant", "Cafe", "Bakery", "Corporate office", "Supplier"), or write the closest specific industry name.

              CRITICAL CONSTRAINT: The industry you choose MUST be logically compatible with Google's category tags.
              For example:
              - If Google's category is "health" or "doctor", it CANNOT be a "car wash", "laundry", or "plumber" — it must be a medical, dental, wellness, or health-related service (e.g. "Dental hygienist", "Dentist").
              - If the business name is "Wheely Clean" but Google classifies it under "health", it is a mobile dental/teeth whitening service, not a car wash.

              Return only the raw name of the industry in English. Do not add any extra text, punctuation, explanation, or HTML.
              `;

                const aiResult = await model.generateContent(prompt);
                const aiResolvedName = aiResult.response
                  .text()
                  .trim()
                  .toLowerCase();

                let bestMatchRecord: {
                  displayName: string;
                  softwareNeeds: string[];
                } | null = null;
                let bestMatchKey = null;

                for (const [key, record] of Object.entries(mappingDict)) {
                  if (record.displayName.toLowerCase() === aiResolvedName) {
                    bestMatchRecord = record;
                    bestMatchKey = key;
                    break;
                  }
                }

                if (!bestMatchRecord) {
                  for (const [key, record] of Object.entries(mappingDict)) {
                    const recName = record.displayName.toLowerCase();
                    if (
                      aiResolvedName.includes(recName) ||
                      recName.includes(aiResolvedName)
                    ) {
                      bestMatchRecord = record;
                      bestMatchKey = key;
                      break;
                    }
                  }
                }

                if (bestMatchRecord && bestMatchKey) {
                  resolvedGcid = bestMatchKey;
                  bizType = mapTypeToBaseline(
                    bestMatchRecord.displayName,
                    allTypes,
                  );
                }
              } catch (aiErr) {
                console.error("Gemini fallback resolution error:", aiErr);
              }

              const finalRecord = mappingDict[resolvedGcid];
              const typeLabel =
                finalRecord?.displayName || p.primaryType
                  ? (finalRecord?.displayName || p.primaryType || "")
                      .replace(/_/g, " ")
                      .replace(/\b\w/g, (c: string) => c.toUpperCase())
                  : "Local Business";

              const presetTools = finalRecord?.softwareNeeds || undefined;
              const cleanTools = (presetTools || [])
                .slice(0, 3)
                .map((t) => t.replace(/\s*Software$/i, ""))
                .join(", ");
              const description = cleanTools
                ? `Presets: ${cleanTools}. Built as a custom unified workspace for your business.`
                : "Custom local business tools. Presets: CRM, Invoicing, and Scheduling.";

              // Resolve any alternative categories found in Google types
              const alternativeCategories: Array<{
                gcid: string;
                typeLabel: string;
                presetTools: string[];
                description: string;
              }> = [];
              (p.types || []).forEach((extra: string) => {
                const extraGcid = `gcid:${extra}`;
                const extraRecord = mappingDict[extraGcid];
                if (extraRecord && extraGcid !== resolvedGcid) {
                  const extraTools = extraRecord.softwareNeeds || [];
                  const extraClean = extraTools
                    .slice(0, 3)
                    .map((t) => t.replace(/\s*Software$/i, ""))
                    .join(", ");
                  alternativeCategories.push({
                    gcid: extraGcid,
                    typeLabel: extraRecord.displayName,
                    presetTools: extraTools,
                    description: extraClean
                      ? `Presets: ${extraClean}. Built as a custom unified workspace for your business.`
                      : "Custom local business tools. Presets: CRM, Invoicing, and Scheduling.",
                  });
                }
              });

              return {
                placeId: p.id,
                name: displayName,
                vicinity,
                primaryType,
                typeLabel,
                bizType,
                gcid: resolvedGcid,
                description,
                presetTools,
                alternativeCategories:
                  alternativeCategories.length > 0
                    ? alternativeCategories
                    : undefined,
              };
            },
          ),
        );
      }
    }

    return NextResponse.json(finalMatches);
  } catch (err) {
    console.error("Places/DataForSEO search exception:", err);
    return NextResponse.json(
      { error: "Internal server error" },
      { status: 500 },
    );
  }
}
