/**
 * One-off importer: wipe existing hot-deal / exclusive-discovery member_offers
 * and re-seed them from the source JSON specs, storing the FULL deal payload
 * (plus matched travel rules) in `raw_config` for lossless round-trip.
 *
 *   apps/core/hot-deals-booking-engine.json  -> member_offers (+ units) + raw_config
 *   apps/core/hot-deals-travel-rules.json    -> raw_config.travel_rules[unit_code]
 *
 * Run from apps/core:  npx tsx src/cmd/import-hot-deals.ts
 */
import { createDBPool } from "@/internal/datastore/index";
import dotenv from "dotenv";
import fs from "fs/promises";
import path from "path";
import { createHash } from "crypto";

dotenv.config();

const config = {
  host: process.env.DATABASE_HOST,
  port: parseInt(process.env.DATABASE_PORT || "5432"),
  database: process.env.DATABASE_NAME,
  user: process.env.DATABASE_USER,
  password: process.env.DATABASE_PWD,
  ssl:
    process.env.DATABASE_HOST !== "localhost" && process.env.DATABASE_HOST !== "127.0.0.1"
      ? { rejectUnauthorized: false }
      : undefined,
};

function stringToUUID(str: string): string {
  const hash = createHash("sha256").update(str).digest("hex");
  return [
    hash.substring(0, 8),
    hash.substring(8, 12),
    "4" + hash.substring(13, 16),
    ((parseInt(hash.substring(16, 18), 16) & 0x3f) | 0x80).toString(16) + hash.substring(18, 20),
    hash.substring(20, 32),
  ].join("-");
}

const clean = (s: string) => (s || "").toLowerCase().replace(/[^a-z0-9]/g, "");

function findResortId(resortNameInJson: string, dbResorts: any[]): string | null {
  const cleanJsonName = clean(resortNameInJson);
  for (const r of dbResorts) if (clean(r.name) === cleanJsonName) return r.id;
  for (const r of dbResorts) {
    if (cleanJsonName.includes(clean(r.name)) || clean(r.name).includes(cleanJsonName)) return r.id;
  }
  return null;
}

async function run() {
  console.log("=== Hot Deals + Travel Rules import ===");
  const db = createDBPool(config);

  try {
    const beRaw = await fs.readFile(path.resolve(process.cwd(), "hot-deals-booking-engine.json"), "utf-8");
    const trRaw = await fs.readFile(path.resolve(process.cwd(), "hot-deals-travel-rules.json"), "utf-8");
    const bookingDeals = JSON.parse(beRaw) as Record<string, any>;
    const travelRules = JSON.parse(trRaw) as Record<string, Record<string, any>>;

    // index travel-rules resorts by cleaned name for fuzzy matching
    const trByClean: Record<string, { key: string; units: Record<string, any> }> = {};
    for (const [k, v] of Object.entries(travelRules)) trByClean[clean(k)] = { key: k, units: v };

    const dbResorts = await db.selectFrom("resorts" as any).select(["id", "name"]).execute();
    console.log(`Resorts in DB: ${dbResorts.length}`);

    await db.transaction().execute(async (trx: any) => {
      // 1) delete existing hot-deal / exclusive-discovery offers (+ their units)
      const existing = await trx
        .selectFrom("member_offers" as any)
        .select(["id"])
        .where("offer_type" as any, "in", ["HOT_DEAL", "EXCLUSIVE_DISCOVERY"])
        .execute();
      const existingIds = existing.map((r: any) => r.id);
      console.log(`Deleting ${existingIds.length} existing offers (+ their units)...`);
      if (existingIds.length) {
        await trx.deleteFrom("member_offer_units" as any).where("member_offer_id" as any, "in", existingIds).execute();
        await trx.deleteFrom("member_offers" as any).where("id" as any, "in", existingIds).execute();
      }

      // 2) import each deal from the booking-engine spec
      let ok = 0;
      let skipped = 0;
      let totalTrUnits = 0;
      let totalUnitsMapped = 0;

      for (const [key, deal] of Object.entries(bookingDeals)) {
        const resortName = deal.resort;
        const resortId = resortName ? findResortId(resortName, dbResorts) : null;
        if (!resortId) {
          console.warn(`  [SKIP] "${key}" — no resort match for "${resortName}"`);
          skipped++;
          continue;
        }

        // match travel rules for this deal's resort + unit codes
        let trMatch = trByClean[clean(resortName)];
        if (!trMatch) {
          const c = clean(resortName);
          for (const ck of Object.keys(trByClean)) {
            if (c && (c.includes(ck) || ck.includes(c))) {
              trMatch = trByClean[ck];
              break;
            }
          }
        }
        const dealTravelRules: Record<string, any> = {};
        const unitCodes: string[] = Array.isArray(deal.unit_code) ? deal.unit_code : [];
        if (trMatch) {
          for (const u of unitCodes) {
            if (trMatch.units[u] !== undefined) dealTravelRules[u] = trMatch.units[u];
          }
        }
        totalTrUnits += Object.keys(dealTravelRules).length;

        const parsedId = stringToUUID(key);
        const offerType = deal.type === "exclusive_offer" ? "EXCLUSIVE_DISCOVERY" : "HOT_DEAL";
        const bookingStart = deal.starts ? String(deal.starts).split(" ")[0] : null;
        const bookingEnd = deal.expires ? String(deal.expires).split(" ")[0] : null;
        const travelStart = deal.travel_between?.[0]?.[0] || deal.min_date || null;
        const travelEnd = deal.travel_between?.[0]?.[1] || deal.max_date || null;

        // full payload lives in raw_config (roles/override/master_info/etc.),
        // with the matched travel rules nested under travel_rules
        const rawConfig = { ...deal, travel_rules: dealTravelRules };

        await trx
          .insertInto("member_offers" as any)
          .values({
            id: parsedId,
            name: deal.master_info?.tagline || key,
            offer_type: offerType,
            description: deal.master_info?.price || null,
            resort_id: resortId,
            is_active: true,
            is_cancellable: false,
            booking_period_start: bookingStart,
            booking_period_end: bookingEnd,
            travel_period_start: travelStart,
            travel_period_end: travelEnd,
            blocked_account_types: [],
            blocked_account_statuses: [],
            blocked_clubs: [],
            allowed_user_types: ["LEGAL_OWNER", "FAMILY_MEMBER"],
            inclusions: deal.inclusions || null,
            terms_and_conditions: deal.terms_conditions || null,
            confirmation_edm_template_ids: [],
            cancellation_edm_template_ids: [],
            publish_mode: "PUBLISHED",
            published_at: new Date(),
            raw_config: rawConfig,
            updated_at: new Date(),
          })
          .execute();

        // map units
        if (unitCodes.length) {
          const dbUnits = await trx
            .selectFrom("resort_units" as any)
            .select(["id", "unit_vp_code"])
            .where("resort_id", "=", resortId)
            .execute();

          let offerValueType = "PERCENTAGE";
          let offerValue = 75;
          const priceStr: string = deal.master_info?.price || "";
          const pctMatch = priceStr.match(/(\d+)%\s*Off/i);
          if (pctMatch) {
            offerValue = parseInt(pctMatch[1]);
            offerValueType = "PERCENTAGE";
          } else if (typeof deal.price === "number" && deal.price > 0) {
            offerValue = deal.price;
            offerValueType = "POINTS";
          }

          const minNights = deal.phase_3_min_night || deal.night || null;
          const maxNights = deal.phase_3_max_night || deal.night || null;

          const rows: any[] = [];
          for (const code of unitCodes) {
            const matched = dbUnits.find(
              (u: any) => (u.unit_vp_code || "").toLowerCase().trim() === code.toLowerCase().trim(),
            );
            if (matched) {
              rows.push({
                member_offer_id: parsedId,
                resort_unit_id: matched.id,
                is_admin_override: true,
                offer_value_type: offerValueType,
                offer_value: offerValue,
                minimum_nights: minNights,
                maximum_nights_per_booking: maxNights,
                maximum_units_per_membership: 2,
                created_at: new Date(),
                updated_at: new Date(),
              });
            }
          }
          if (rows.length) {
            await trx.insertInto("member_offer_units" as any).values(rows).execute();
            totalUnitsMapped += rows.length;
          }
        }

        ok++;
        console.log(
          `  [OK] ${key} -> ${resortName} | units:${unitCodes.length} tr_units:${Object.keys(dealTravelRules).length}`,
        );
      }

      console.log(
        `\nImported ${ok} deals (skipped ${skipped}); mapped ${totalUnitsMapped} units; ${totalTrUnits} unit travel-rule sets attached.`,
      );
    });

    console.log("=== DONE (committed) ===");
  } catch (err) {
    console.error("Import FAILED (rolled back):", err);
    process.exitCode = 1;
  } finally {
    await db.destroy();
  }
}

run();
