import { logError } from "@/lib/logger";
import { error as errorResponse, success } from "@/lib/response";
import { HotDealsService } from "@/v1/services/admin/hot-deals/hot-deals.service";
import { env } from "@/lib/env";
import type { Context } from "hono";

const formatToLocalDate = (d: any): string => {
  if (!d) return "";
  const dateObj = d instanceof Date ? d : new Date(d);
  if (isNaN(dateObj.getTime())) return "";
  const yyyy = dateObj.getFullYear();
  const mm = String(dateObj.getMonth() + 1).padStart(2, "0");
  const dd = String(dateObj.getDate()).padStart(2, "0");
  return `${yyyy}-${mm}-${dd}`;
};

const stripHtml = (html: string | null | undefined): string => {
  if (!html) return "";
  let text = html.replace(/<li[^>]*>/gi, "• ");
  text = text.replace(/<(p|div|br|h[1-6]|ol|ul)[^>]*>/gi, "");
  text = text.replace(/<\/(p|div|ol|ul|li)>/gi, "\n");
  text = text.replace(/<[^>]*>/g, "");
  text = text
    .replace(/&nbsp;/gi, " ")
    .replace(/&amp;/gi, "&")
    .replace(/&lt;/gi, "<")
    .replace(/&gt;/gi, ">")
    .replace(/&#8226;/gi, "•")
    .replace(/&bull;/gi, "•")
    .replace(/&rsquo;/gi, "'")
    .replace(/&lsquo;/gi, "'")
    .replace(/&rdquo;/gi, '"')
    .replace(/&ldquo;/gi, '"')
    .replace(/&#39;/gi, "'")
    .replace(/&quot;/gi, '"');
  text = text.replace(/\n\s*\n/g, "\n");
  return text.trim();
};

export const listHotDealsHandler = async (c: Context) => {
  try {
    const db = c.get("datastore");
    const service = HotDealsService.getInstance();
    const result = await service.listDeals(db);
    return success(c, result.data, result.message, 200);
  } catch (err: any) {
    logError("Error in listHotDealsHandler:", err);
    return errorResponse(c, err?.message || "Failed to list hot deals", 500);
  }
};

export const getHotDealHandler = async (c: Context) => {
  try {
    const id: any = c.req.param("id");
    const db = c.get("datastore");
    const service = HotDealsService.getInstance();
    const result = await service.getDeal(db, id);
    if (!result.success) {
      return errorResponse(c, result.message, 404);
    }
    return success(c, result.data, result.message, 200);
  } catch (err: any) {
    logError("Error in getHotDealHandler:", err);
    return errorResponse(c, err?.message || "Failed to get hot deal", 500);
  }
};

export const createHotDealHandler = async (c: Context) => {
  try {
    const body = await c.req.json();
    const { id, deal } = body as { id: string; deal: Record<string, any> };
    if (!id || typeof id !== "string") {
      return errorResponse(c, "Deal ID is required", 400);
    }
    const db = c.get("datastore");
    const service = HotDealsService.getInstance();
    const result = await service.createDeal(db, id, deal ?? {});
    if (!result.success) {
      return errorResponse(c, result.message, 409);
    }
    return success(c, result.data, result.message, 201);
  } catch (err: any) {
    logError("Error in createHotDealHandler:", err);
    return errorResponse(c, err?.message || "Failed to create hot deal", 500);
  }
};

export const updateHotDealHandler = async (c: Context) => {
  try {
    const id: any = c.req.param("id");
    const body = await c.req.json();
    const db = c.get("datastore");
    const service = HotDealsService.getInstance();
    const result = await service.updateDeal(db, id, body);
    if (!result.success) {
      return errorResponse(c, result.message, 404);
    }
    return success(c, result.data, result.message, 200);
  } catch (err: any) {
    logError("Error in updateHotDealHandler:", err);
    return errorResponse(c, err?.message || "Failed to update hot deal", 500);
  }
};

export const deleteHotDealHandler = async (c: Context) => {
  try {
    const id: any = c.req.param("id");
    const db = c.get("datastore");
    const service = HotDealsService.getInstance();
    const result = await service.deleteDeal(db, id);
    if (!result.success) {
      return errorResponse(c, result.message, 404);
    }
    return success(c, result.data, result.message, 200);
  } catch (err: any) {
    logError("Error in deleteHotDealHandler:", err);
    return errorResponse(c, err?.message || "Failed to delete hot deal", 500);
  }
};

/**
 * Merge the console-saved `raw_config` payload over the column-derived deal
 * so the emitted JSON matches exactly what an admin entered. Columns win for
 * the fields they own (resort / booking window / travel window / unit list);
 * `travel_rules` and `occp_price` are excluded — travel_rules is served by the
 * public travel-rules feed, occp_price is a dead legacy field.
 */
const mergeRawConfigIntoDeal = (
  derived: Record<string, any>,
  rawConfig: any,
): Record<string, any> => {
  const raw = rawConfig && typeof rawConfig === "object" ? rawConfig : {};
  const rawDeal: Record<string, any> = { ...raw };
  delete rawDeal.travel_rules;
  delete rawDeal.occp_price;

  const merged: Record<string, any> = { ...derived, ...rawDeal };
  merged.master_info = { ...derived.master_info, ...(rawDeal.master_info || {}) };

  merged.resort = derived.resort;
  merged.unit_code = derived.unit_code;
  if (derived.starts) merged.starts = derived.starts;
  if (derived.expires) merged.expires = derived.expires;
  if (Array.isArray(derived.travel_between) && derived.travel_between.length) {
    merged.travel_between = derived.travel_between;
  }
  return merged;
};

const getUnitCodesForOffer = async (db: any, offerId: string): Promise<string[]> => {
  const units = await db
    .selectFrom("member_offer_units as mou")
    .innerJoin("resort_units as ru", "ru.id", "mou.resort_unit_id")
    .select("ru.unit_vp_code as unit_vp_code")
    .where("mou.member_offer_id", "=", offerId)
    .execute();
  return units.map((u: any) => u.unit_vp_code).filter(Boolean);
};

export const listPublicHotDealsHandler = async (c: Context) => {
  try {
    const apiKey = c.req.header("x-api-key") || c.req.header("X-API-KEY");
    const expectedKey = env.GetString("WP_HOT_DEALS_API_KEY");
    if (!apiKey || apiKey !== expectedKey) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    const db = c.get("datastore");

    const dbOffers = await db
      .selectFrom("member_offers as mo")
      .innerJoin("resorts as r", "r.id", "mo.resort_id")
      .select([
        "mo.id",
        "mo.name",
        "mo.description",
        "mo.offer_type",
        "mo.booking_period_start",
        "mo.booking_period_end",
        "mo.travel_period_start",
        "mo.travel_period_end",
        "mo.inclusions",
        "mo.terms_and_conditions",
        "mo.allowed_user_types",
        "mo.raw_config",
        "mo.publish_mode",
        "mo.is_active",
        "r.name as resort_name",
        "r.code as resort_code",
        "r.country as resort_country",
        "r.cover_image as resort_cover_image",
        "r.website_url as resort_website_url",
      ])
      .where("mo.offer_type", "in", ["HOT_DEAL", "EXCLUSIVE_DISCOVERY"])
      .where("mo.publish_mode", "=", "PUBLISHED")
      .where("mo.is_active", "=", true)
      .execute();

    const data: Record<string, any> = {};

    for (const mo of dbOffers) {
      const tempRegion = mo.resort_country || "";

      const travelStart = mo.travel_period_start ? formatToLocalDate(mo.travel_period_start) : "";
      const travelEnd = mo.travel_period_end ? formatToLocalDate(mo.travel_period_end) : "";
      const travelRangeStr = travelStart && travelEnd ? `Travel Dates : ${travelStart} to ${travelEnd}` : "";

      const regionTitle = tempRegion ? `${tempRegion.toUpperCase()} HOT DEAL` : "HOT DEAL";
      const cleanDescription = stripHtml(mo.description);
      const unitCodes = await getUnitCodesForOffer(db, mo.id);

      const derived = {
        resort: mo.resort_name,
        travel_between: travelStart && travelEnd ? [[travelStart, travelEnd]] : [],
        new: "",
        starts: mo.booking_period_start ? `${formatToLocalDate(mo.booking_period_start)} 00:00:00` : "",
        expires: mo.booking_period_end ? `${formatToLocalDate(mo.booking_period_end)} 23:59:59` : "",
        roles: mo.allowed_user_types || [],
        unit_code: unitCodes,
        night: 7,
        occ_up_to: 6,
        type: mo.offer_type === "EXCLUSIVE_DISCOVERY" ? "exclusive_offer" : "hot_deal",
        inclusions: stripHtml(mo.inclusions),
        terms_conditions: stripHtml(mo.terms_and_conditions),
        thumbnail: mo.resort_cover_image || "",
        destination_profile_url: mo.resort_website_url || "",
        master_info: {
          tagline: mo.name,
          title: regionTitle,
          price: cleanDescription || "Special Points Offer",
          details: cleanDescription ? [cleanDescription] : [],
          travel: travelRangeStr,
          booking: "",
          check_in: travelStart,
          check_out: travelEnd,
        },
      };

      data[mo.id] = mergeRawConfigIntoDeal(derived, mo.raw_config);
    }

    return c.json(data, 200);
  } catch (err: any) {
    logError("Error in listPublicHotDealsHandler:", err);
    return c.json({ error: err?.message || "Failed to list public hot deals" }, 500);
  }
};

/**
 * Public travel-rules feed — mirrors the legacy `internal_exchange_travel_rules`
 * JSON shape `{ [resortName]: { [unitCode]: Rule[] } }`, assembled from each
 * published offer's `raw_config.travel_rules` (keyed by unit code) nested under
 * its resort name. Consumed by PHP's `internal_exchange_get_travel_rules()`.
 */
export const listPublicTravelRulesHandler = async (c: Context) => {
  try {
    const apiKey = c.req.header("x-api-key") || c.req.header("X-API-KEY");
    const expectedKey = env.GetString("WP_HOT_DEALS_API_KEY");
    if (!apiKey || apiKey !== expectedKey) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    const db = c.get("datastore");

    const dbOffers = await db
      .selectFrom("member_offers as mo")
      .innerJoin("resorts as r", "r.id", "mo.resort_id")
      .select([
        "mo.raw_config",
        "r.name as resort_name",
      ])
      .where("mo.offer_type", "in", ["HOT_DEAL", "EXCLUSIVE_DISCOVERY"])
      .where("mo.publish_mode", "=", "PUBLISHED")
      .where("mo.is_active", "=", true)
      .execute();

    const data: Record<string, Record<string, any[]>> = {};

    for (const mo of dbOffers) {
      const resortName = mo.resort_name;
      if (!resortName) continue;

      const raw = mo.raw_config && typeof mo.raw_config === "object" ? mo.raw_config : {};
      const travelRules = raw.travel_rules;
      if (!travelRules || typeof travelRules !== "object") continue;

      if (!data[resortName]) data[resortName] = {};

      for (const [unitCode, rules] of Object.entries(travelRules)) {
        const ruleList = Array.isArray(rules) ? rules : [rules];
        // Merge with any rules already collected for the same resort+unit from
        // another offer, rather than clobbering them.
        data[resortName][unitCode] = [
          ...(data[resortName][unitCode] || []),
          ...ruleList,
        ];
      }
    }

    return c.json(data, 200);
  } catch (err: any) {
    logError("Error in listPublicTravelRulesHandler:", err);
    return c.json({ error: err?.message || "Failed to list public travel rules" }, 500);
  }
};

export const listPublicHotDealsListHandler = async (c: Context) => {
  try {
    const apiKey = c.req.header("x-api-key") || c.req.header("X-API-KEY");
    const expectedKey = env.GetString("WP_HOT_DEALS_API_KEY");
    if (!apiKey || apiKey !== expectedKey) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    const region = c.req.query("region");
    const hotDealId = c.req.query("id");
    const testDate = c.req.query("test_date") || c.req.header("x-test-date");
    const bypassRoles = c.req.query("bypass_roles") === "1" || c.req.header("x-bypass-roles") === "1";

    const today = testDate ? new Date(testDate) : new Date();
    const todayStr = formatToLocalDate(today);

    const db = c.get("datastore");

    let dbQuery = db
      .selectFrom("member_offers as mo")
      .innerJoin("resorts as r", "r.id", "mo.resort_id")
      .select([
        "mo.id",
        "mo.name",
        "mo.description",
        "mo.booking_period_start",
        "mo.booking_period_end",
        "mo.travel_period_start",
        "mo.travel_period_end",
        "mo.inclusions",
        "mo.terms_and_conditions",
        "mo.publish_mode",
        "mo.is_active",
        "r.name as resort_name",
        "r.code as resort_code",
        "r.country as resort_country",
        "r.cover_image as resort_cover_image",
        "r.website_url as resort_website_url",
      ])
      .where("mo.offer_type", "=", "HOT_DEAL")
      .where("mo.publish_mode", "=", "PUBLISHED")
      .where("mo.is_active", "=", true);

    if (!bypassRoles) {
      dbQuery = dbQuery
        .where("mo.booking_period_start", "<=", todayStr)
        .where("mo.booking_period_end", ">=", todayStr);
    }

    if (hotDealId) {
      dbQuery = dbQuery.where("mo.id", "=", hotDealId);
    }

    if (region) {
      dbQuery = dbQuery.where("r.country", "ilike", region);
    }

    const dbOffers = await dbQuery.execute();

    const list: any[] = [];
    const stickyResorts = ["karma chateau de samary"];

    for (const mo of dbOffers) {
      const resortNameClean = (mo.resort_name || "").toLowerCase().trim();
      const resortCode = mo.resort_code || "";
      const tempRegion = mo.resort_country || "";

      const travelStart = mo.travel_period_start ? formatToLocalDate(mo.travel_period_start) : "";
      const travelEnd = mo.travel_period_end ? formatToLocalDate(mo.travel_period_end) : "";
      const travelRangeStr = travelStart && travelEnd ? `Travel Dates : ${travelStart} to ${travelEnd}` : "";

      const regionTitle = tempRegion ? `${tempRegion.toUpperCase()} HOT DEAL` : "HOT DEAL";
      const cleanDescription = stripHtml(mo.description);

      const formattedOffer = {
        id: mo.id,
        resort: mo.resort_name,
        resort_code: resortCode,
        expires: mo.booking_period_end ? `${formatToLocalDate(mo.booking_period_end)} 23:59:59` : "",
        submit_request: false,
        memberTravelling: true,
        guestTravelling: false,
        tagline: mo.name,
        title: regionTitle,
        price: cleanDescription || "Special Points Offer",
        details: cleanDescription ? [cleanDescription] : [],
        travel: travelRangeStr,
        booking: "",
        check_in: travelStart,
        check_out: travelEnd,
        inclusions: stripHtml(mo.inclusions),
        terms_conditions: stripHtml(mo.terms_and_conditions),
        thumbnail: mo.resort_cover_image || "",
        destination_profile_url: mo.resort_website_url || "",
      };

      if (stickyResorts.includes(resortNameClean)) {
        list.unshift(formattedOffer);
      } else {
        list.push(formattedOffer);
      }
    }

    if (hotDealId && list.length > 0) {
      return c.json(list[0], 200);
    }

    return c.json(list, 200);
  } catch (err: any) {
    logError("Error in listPublicHotDealsListHandler:", err);
    return c.json({ error: err?.message || "Failed to list hot deals" }, 500);
  }
};

export const getPublicHotDealsDetailHandler = async (c: Context) => {
  try {
    const apiKey = c.req.header("x-api-key") || c.req.header("X-API-KEY");
    const expectedKey = env.GetString("WP_HOT_DEALS_API_KEY");
    if (!apiKey || apiKey !== expectedKey) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    const hotDealId = c.req.param("id");
    if (!hotDealId) {
      return c.json({ message: "One or more fields have an error. hot_deal_id is required." }, 400);
    }

    const testDate = c.req.query("test_date") || c.req.header("x-test-date");
    const bypassRoles = c.req.query("bypass_roles") === "1" || c.req.header("x-bypass-roles") === "1";

    const today = testDate ? new Date(testDate) : new Date();
    const todayStr = formatToLocalDate(today);

    const db = c.get("datastore");

    let dbQuery = db
      .selectFrom("member_offers as mo")
      .innerJoin("resorts as r", "r.id", "mo.resort_id")
      .select([
        "mo.id",
        "mo.name",
        "mo.description",
        "mo.booking_period_start",
        "mo.booking_period_end",
        "mo.travel_period_start",
        "mo.travel_period_end",
        "mo.inclusions",
        "mo.terms_and_conditions",
        "mo.publish_mode",
        "mo.is_active",
        "r.name as resort_name",
        "r.code as resort_code",
        "r.country as resort_country",
        "r.cover_image as resort_cover_image",
        "r.website_url as resort_website_url",
      ])
      .where("mo.id", "=", hotDealId)
      .where("mo.offer_type", "=", "HOT_DEAL")
      .where("mo.publish_mode", "=", "PUBLISHED")
      .where("mo.is_active", "=", true);

    if (!bypassRoles) {
      dbQuery = dbQuery
        .where("mo.booking_period_start", "<=", todayStr)
        .where("mo.booking_period_end", ">=", todayStr);
    }

    const mo = await dbQuery.executeTakeFirst();
    if (!mo) {
      return c.json({ message: "Offer is not found." }, 400);
    }

    const tempRegion = mo.resort_country || "";
    const travelStart = mo.travel_period_start ? formatToLocalDate(mo.travel_period_start) : "";
    const travelEnd = mo.travel_period_end ? formatToLocalDate(mo.travel_period_end) : "";
    const travelRangeStr = travelStart && travelEnd ? `Travel Dates : ${travelStart} to ${travelEnd}` : "";

    const regionTitle = tempRegion ? `${tempRegion.toUpperCase()} HOT DEAL` : "HOT DEAL";
    const cleanDescription = stripHtml(mo.description);

    const formattedOffer = {
      id: mo.id,
      resort: mo.resort_name,
      resort_code: mo.resort_code || "",
      expires: mo.booking_period_end ? `${formatToLocalDate(mo.booking_period_end)} 23:59:59` : "",
      title: regionTitle,
      submit_request: false,
      memberTravelling: true,
      guestTravelling: false,
      tagline: mo.name,
      price: cleanDescription || "Special Points Offer",
      details: cleanDescription ? [cleanDescription] : [],
      travel: travelRangeStr,
      booking: "",
      check_in: travelStart,
      check_out: travelEnd,
      inclusions: stripHtml(mo.inclusions),
      terms_conditions: stripHtml(mo.terms_and_conditions),
      thumbnail: mo.resort_cover_image || "",
      destination_profile_url: mo.resort_website_url || "",
    };

    return c.json(formattedOffer, 200);
  } catch (err: any) {
    logError("Error in getPublicHotDealsDetailHandler:", err);
    return c.json({ error: err?.message || "Failed to get hot deal detail" }, 500);
  }
};

export const listPublicHotDealsRegionsHandler = async (c: Context) => {
  try {
    const apiKey = c.req.header("x-api-key") || c.req.header("X-API-KEY");
    const expectedKey = env.GetString("WP_HOT_DEALS_API_KEY");
    if (!apiKey || apiKey !== expectedKey) {
      return c.json({ error: "Unauthorized" }, 401);
    }

    const testDate = c.req.query("test_date") || c.req.header("x-test-date");
    const bypassRoles = c.req.query("bypass_roles") === "1" || c.req.header("x-bypass-roles") === "1";

    const today = testDate ? new Date(testDate) : new Date();
    const todayStr = formatToLocalDate(today);

    const db = c.get("datastore");

    let dbQuery = db
      .selectFrom("member_offers as mo")
      .innerJoin("resorts as r", "r.id", "mo.resort_id")
      .select("r.country as resort_country")
      .distinct()
      .where("mo.offer_type", "=", "HOT_DEAL")
      .where("mo.publish_mode", "=", "PUBLISHED")
      .where("mo.is_active", "=", true);

    if (!bypassRoles) {
      dbQuery = dbQuery
        .where("mo.booking_period_start", "<=", todayStr)
        .where("mo.booking_period_end", ">=", todayStr);
    }

    const countriesResult = await dbQuery.execute();
    const regionList = countriesResult
      .map((r: any) => r.resort_country)
      .filter((c: any) => !!c)
      .sort();

    return c.json(regionList, 200);
  } catch (err: any) {
    logError("Error in listPublicHotDealsRegionsHandler:", err);
    return c.json({ error: err?.message || "Failed to list hot deals regions" }, 500);
  }
};

