const fs = require('fs');
const path = require('path');
const { addBaseUrlToImages } = require("../utils/imageHelper");
const Booking = require("../models/booking");

// -------------------- Public helpers --------------------
const getBookingData = async () => {
  const booking = await Booking.findOne().sort({ updatedAt: -1 });
  return booking ? (booking.toObject ? booking.toObject() : booking) : null;
};

// Load static booking JSON from `data/booking.json` (if present)
const loadStaticBooking = () => {
  try {
    const p = path.join(__dirname, '..', 'data', 'booking.json');
    if (!fs.existsSync(p)) return null;
    const raw = fs.readFileSync(p, 'utf8');
    return JSON.parse(raw);
  } catch (e) {
    console.error('booking.loadStaticBooking error:', e && e.message);
    return null;
  }
};

// Normalize booking shape: ensure configuration exists with discounts/vouchers
const normalizeBookingShape = (booking) => {
  if (!booking || typeof booking !== 'object') return booking;
  const b = JSON.parse(JSON.stringify(booking));

  if (!b.configuration || typeof b.configuration !== 'object') {
    b.configuration = { currency: 'USD', discounts: [], vouchers: [] };
  }

  // Ensure configuration.discounts and configuration.vouchers exist
  if (!Array.isArray(b.configuration.discounts)) {
    b.configuration.discounts = [];
  }
  if (!Array.isArray(b.configuration.vouchers)) {
    b.configuration.vouchers = [];
  }

  return b;
};

// Deep merge: properties from `overrides` replace / merge into `base`.
const deepMerge = (base, overrides) => {
  if (overrides === undefined) return base;
  if (base === undefined || base === null) return overrides;
  if (Array.isArray(overrides)) return overrides;
  if (typeof overrides !== 'object' || overrides === null) return overrides;
  const out = Object.assign({}, base);
  Object.keys(overrides).forEach((k) => {
    if (Array.isArray(overrides[k]) || typeof overrides[k] !== 'object' || overrides[k] === null) {
      out[k] = overrides[k];
    } else {
      out[k] = deepMerge(base[k], overrides[k]);
    }
  });
  return out;
};

// Ensure booking data fields have the expected shapes to avoid runtime errors
const sanitizeBookingData = (raw) => {
  const defaults = {
    hero: { title: '', backgroundImage: '' },
    searchBar: { locationLabel: '', holidaySeasonLabel: '', searchButtonText: '' },
    filterPanel: {
      title: '',
      priceTitle: '',
      priceLabel: '',
      pricePlaceholder: '',
      priceMin: 0,
      priceMax: 0,
      ageTitle: '',
      ageMin: 0,
      ageMax: 0,
      ageSelectPlaceholder: '',
      activitiesTitle: '',
      ratingTitle: '',
      ratingOptions: [],
      resetButtonText: ''
    },
    programs: [],
    holidays: [],
    locations: [],
    camps: [],
    configuration: { currency: 'USD', discounts: [], vouchers: [] },
    formSteps: [],
    validation: {}
  };

  if (!raw || typeof raw !== 'object') return defaults;

  // Use raw data first, then fill in missing fields with defaults
  const safe = Object.assign({}, raw);

  // Ensure nested objects/arrays have correct types (use raw data if valid, otherwise defaults)
  safe.hero = (safe.hero && typeof safe.hero === 'object') ? safe.hero : defaults.hero;
  safe.searchBar = (safe.searchBar && typeof safe.searchBar === 'object') ? safe.searchBar : defaults.searchBar;
  safe.filterPanel = (safe.filterPanel && typeof safe.filterPanel === 'object') ? safe.filterPanel : defaults.filterPanel;

  if (!Array.isArray(safe.filterPanel.ratingOptions)) safe.filterPanel.ratingOptions = defaults.filterPanel.ratingOptions;

  safe.programs = Array.isArray(safe.programs) ? safe.programs : defaults.programs;
  safe.holidays = Array.isArray(safe.holidays) ? safe.holidays : defaults.holidays;
  safe.locations = Array.isArray(safe.locations) ? safe.locations : defaults.locations;
  safe.camps = Array.isArray(safe.camps) ? safe.camps : defaults.camps;
  
  // Ensure configuration has proper structure
  if (!safe.configuration || typeof safe.configuration !== 'object') {
    safe.configuration = defaults.configuration;
  }
  if (!Array.isArray(safe.configuration.discounts)) {
    safe.configuration.discounts = defaults.configuration.discounts;
  }
  if (!Array.isArray(safe.configuration.vouchers)) {
    safe.configuration.vouchers = defaults.configuration.vouchers;
  }
  
  // Ensure formSteps and validation have correct types
  safe.formSteps = Array.isArray(safe.formSteps) ? safe.formSteps : defaults.formSteps;
  safe.validation = (safe.validation && typeof safe.validation === 'object' && !Array.isArray(safe.validation)) ? safe.validation : defaults.validation;

  return safe;
};

// Safe JSON parse with better error handling - handles double-encoded JSON and JS object notation
const safeParse = (value, fieldName = 'unknown') => {
  // If already an object or array, return as-is
  if (typeof value === 'object' && value !== null) {
    return value;
  }
  
  // If string, try to parse
  if (typeof value === 'string') {
    try {
      let cleaned = value.trim();
      
      // Check if it looks like JavaScript object notation (has single quotes or unquoted keys)
      if (cleaned.includes("'") || /\{\s*\w+:/.test(cleaned)) {
        console.warn(`safeParse: Converting JS notation to JSON for "${fieldName}"`);
        
        // Aggressive conversion approach
        cleaned = cleaned
          .replace(/'/g, '"')                    // Replace ALL single quotes with double quotes
          .replace(/\r?\n|\r/g, ' ')            // Remove all newlines
          .replace(/\s+/g, ' ')                  // Normalize multiple spaces to single space
          .replace(/,(\s*[}\]])/g, '$1')        // Remove trailing commas before } or ]
          .replace(/([{,]\s*)(\w+):/g, '$1"$2":'); // Quote unquoted object keys
      }
      
      // Try parsing
      let parsed = JSON.parse(cleaned);
      
      // If result is still a string, try parsing again (double-encoded)
      if (typeof parsed === 'string') {
        console.warn(`safeParse: Double-encoded JSON detected for "${fieldName}"`);
        parsed = JSON.parse(parsed);
      }
      
      return parsed;
    } catch (e) {
      console.error(`safeParse: Failed to parse field "${fieldName}"`, {
        error: e.message,
        valuePreview: value.substring(0, 200)
      });
      
      throw new Error(`Invalid JSON format for field: ${fieldName}. Error: ${e.message}`);
    }
  }
  
  // For other types, return empty array or object
  console.warn(`safeParse: Unexpected type for field "${fieldName}": ${typeof value}`);
  return Array.isArray(value) ? [] : {};
};

// Validate booking data structure
const validateBookingData = (data) => {
  const errors = [];
  
  // Check required fields
  if (!data.hero || typeof data.hero !== 'object') {
    errors.push('Hero data is required and must be an object');
  }
  
  if (!data.searchBar || typeof data.searchBar !== 'object') {
    errors.push('SearchBar data is required and must be an object');
  }
  
  if (!data.filterPanel || typeof data.filterPanel !== 'object') {
    errors.push('FilterPanel data is required and must be an object');
  }
  
  // Validate arrays
  if (data.programs && !Array.isArray(data.programs)) {
    errors.push('Programs must be an array');
  }
  
  if (data.holidays && !Array.isArray(data.holidays)) {
    errors.push('Holidays must be an array');
  }
  
  if (data.locations && !Array.isArray(data.locations)) {
    errors.push('Locations must be an array');
  }
  
  if (data.camps && !Array.isArray(data.camps)) {
    errors.push('Camps must be an array');
  }

  // Validate configuration structure
  if (data.configuration) {
    if (typeof data.configuration !== 'object') {
      errors.push('Configuration must be an object');
    } else {
      if (data.configuration.discounts && !Array.isArray(data.configuration.discounts)) {
        errors.push('Configuration.discounts must be an array');
      }
      if (data.configuration.vouchers && !Array.isArray(data.configuration.vouchers)) {
        errors.push('Configuration.vouchers must be an array');
      }
    }
  }

  // Validate formSteps and validation structure if provided
  if (data.formSteps && !Array.isArray(data.formSteps)) {
    errors.push('formSteps must be an array');
  }

  if (data.validation && (typeof data.validation !== 'object' || Array.isArray(data.validation))) {
    errors.push('validation must be an object');
  }
  
  return {
    isValid: errors.length === 0,
    errors
  };
};

// -------------------- Public endpoints --------------------
// Public endpoint: return Booking JSON
exports.page = async (req, res) => {
  try {
    const dbBooking = await getBookingData();
    const staticBooking = loadStaticBooking();

    // Normalize shapes so `configuration.discounts`/`configuration.vouchers` exist
    const normStatic = normalizeBookingShape(staticBooking);
    const normDb = normalizeBookingShape(dbBooking);

    // Build final payload according to BOOKING_MODE env var
    const finalBooking = getFinalBooking(normStatic, normDb);

    if (!finalBooking) {
      return res.status(404).json({ 
        error: "No booking data found",
        message: "Please configure booking data in admin panel" 
      });
    }

    const baseUrl = process.env.BACKEND_URL || `${req.protocol}://${req.get('host')}`;
    const processed = addBaseUrlToImages(finalBooking, baseUrl);

    return res.json(processed);
  } catch (err) {
    console.error("booking.page error:", err);
    return res.status(500).json({ 
      error: "Error loading booking data",
      message: process.env.NODE_ENV === 'development' ? err.message : undefined
    });
  }
};


// API endpoint to return booking JSON
exports.api = async (req, res) => {
  try {
    const dbBooking = await getBookingData();
    const staticBooking = loadStaticBooking();

    // Normalize shapes so `configuration.discounts`/`configuration.vouchers` exist
    const normStatic = normalizeBookingShape(staticBooking);
    const normDb = normalizeBookingShape(dbBooking);

    const finalBooking = getFinalBooking(normStatic, normDb);

    if (!finalBooking) {
      return res.status(404).json({ 
        error: "No booking data found",
        message: "Please configure booking data in admin panel" 
      });
    }

    const baseUrl = process.env.BACKEND_URL || `${req.protocol}://${req.get('host')}`;
    const processed = addBaseUrlToImages(finalBooking, baseUrl);

    return res.json(processed);
  } catch (err) {
    console.error("booking.api error:", err);
    return res.status(500).json({ 
      error: "Error loading booking data",
      message: process.env.NODE_ENV === 'development' ? err.message : undefined
    });
  }
};

// -------------------- Admin endpoints --------------------
// Display Booking management page
exports.index = async (req, res) => {
  try {
    const dbBooking = await getBookingData();
    const staticBooking = loadStaticBooking();

    // Merge static booking with DB data (use same merge logic as public endpoints)
    const normStatic = normalizeBookingShape(staticBooking);
    const normDb = normalizeBookingShape(dbBooking);
    const mergedData = getFinalBooking(normStatic, normDb);
    
    // Normalize again after merge to ensure discounts/vouchers are synced to top-level
    const data = normalizeBookingShape(mergedData);

    // Sanitize data to ensure nested fields are objects/arrays (fixes malformed DB entries)
    const safeData = sanitizeBookingData(data);
    
    res.render("admin/booking/index", {
      layout: "layouts/main",
      title: "Booking Management",
      data: safeData,
      frontendUrl: process.env.FRONTEND_URL || req.protocol + "://" + req.get("host"),
      currentPath: req.path,
      user: req.session.user,
    });
  } catch (err) {
    console.error("booking.index error:", err);
    req.flash("error_msg", "Error loading booking page");
    res.redirect("/admin/dashboard");
  }
};

// Update booking data
exports.update = async (req, res) => {
  try {
    const { id } = req.params;
     // ADD THIS DEBUG LOG
    console.log('=== RAW REQUEST BODY ===');
    console.log('Discounts type:', typeof req.body.discounts);
    console.log('Discounts first 500 chars:', req.body.discounts?.substring(0, 500));
    console.log('Vouchers type:', typeof req.body.vouchers);
    console.log('Vouchers first 500 chars:', req.body.vouchers?.substring(0, 500));
    console.log('========================');
    const {
      hero,
      searchBar,
      filterPanel,
      programs,
      holidays,
      locations,
      camps,
      discounts,
      vouchers,
      formSteps,
      validation: validationRaw
    } = req.body;

    // Parse JSON strings
    const errors = [];
    let updateData = {};

    try {
      console.log('Raw discounts from req.body:', typeof discounts, discounts);
      console.log('Raw vouchers from req.body:', typeof vouchers, vouchers);
      
      const parsedDiscounts = safeParse(discounts, 'discounts');
      const parsedVouchers = safeParse(vouchers, 'vouchers');
      
      console.log('Parsed discounts:', typeof parsedDiscounts, Array.isArray(parsedDiscounts), parsedDiscounts);
      console.log('Parsed vouchers:', typeof parsedVouchers, Array.isArray(parsedVouchers), parsedVouchers);
      
      updateData = {
        hero: safeParse(hero, 'hero'),
        searchBar: safeParse(searchBar, 'searchBar'),
        filterPanel: safeParse(filterPanel, 'filterPanel'),
        programs: safeParse(programs, 'programs'),
        holidays: safeParse(holidays, 'holidays'),
        locations: safeParse(locations, 'locations'),
        camps: safeParse(camps, 'camps'),
        formSteps: safeParse(formSteps, 'formSteps'),
        validation: safeParse(validationRaw, 'validation'),
        configuration: {
          currency: 'USD',
          discounts: parsedDiscounts,
          vouchers: parsedVouchers
        }
      };
    } catch (parseError) {
      console.error('booking.update: Parse error', parseError);
      req.flash("error_msg", `Data processing error: ${parseError.message}`);
      return req.session.save(() => res.redirect("/admin/booking"));
    }

    // Validate data structure
    const validation = validateBookingData(updateData);
    if (!validation.isValid) {
      console.error('booking.update: Validation failed', validation.errors);
      req.flash("error_msg", `Validation failed: ${validation.errors[0]}`);
      return req.session.save(() => res.redirect("/admin/booking"));
    }

    console.log('Final updateData keys:', Object.keys(updateData));
    console.log('updateData.discounts:', updateData.discounts);
    console.log('updateData.configuration:', updateData.configuration);

    // CRITICAL: Remove any top-level discounts/vouchers to prevent schema conflicts
    // These should ONLY exist in configuration object
    delete updateData.discounts;
    delete updateData.vouchers;

    // Update or create booking document
    let result;
    try {
      if (id && id !== 'undefined') {
        result = await Booking.findByIdAndUpdate(
          id, 
          {
            ...updateData,
            $unset: { discounts: "", vouchers: "" }  // Remove old top-level fields
          }, 
           { 
        new: true, 
        runValidators: false,  // TẮT validator để tránh lỗi cast
        strict: false          // TẮT strict mode
      }
        );
        
        if (!result) {
          req.flash("error_msg", "Booking document not found");
          return req.session.save(() => res.redirect("/admin/booking"));
        }
      } else {
        // Upsert: update existing or create new
        result = await Booking.findOneAndUpdate(
          {}, 
          {
            ...updateData,
            $unset: { discounts: "", vouchers: "" }  // Remove old top-level fields
          }, 
           { 
        upsert: true, 
        new: true, 
        runValidators: false,  // TẮT validator
        strict: false          // TẮT strict mode
      }
        );
      }

      req.flash("success_msg", "Booking data updated successfully");
      return req.session.save(() => res.redirect("/admin/booking"));
    } catch (dbError) {
      console.error("booking.update: Database error", dbError);
      req.flash("error_msg", `Database error: ${dbError.message || "Unknown"}`);
      return req.session.save(() => res.redirect("/admin/booking"));
    }
  } catch (err) {
    console.error("booking.update error:", err);
    req.flash("error_msg", `Update error: ${err.message || "Unknown"}`);
    return req.session.save(() => res.redirect("/admin/booking"));
  }
};

// Booking selection mode: 'merge' (default) = static base, DB overrides;
// 'static' = use `data/booking.json` only; 'db' = use DB only.
const getFinalBooking = (staticBooking, dbBooking) => {
  const mode = (process.env.BOOKING_MODE || 'merge').toLowerCase();
  if (mode === 'static') return staticBooking || dbBooking || null;
  if (mode === 'db') return dbBooking || staticBooking || null;
  // default: merge static (base) with DB overrides
  // If both static and db present, attempt to map DB primitive lists (e.g. ["915"]) to
  // full objects from the static file (e.g. {id:"915", name:..., type:...}).
  const mapDbPrimitivesToObjects = (db, stat) => {
    if (!db || !stat) return db;
    const dbCfg = db.configuration || {};
    const statCfg = stat.configuration || {};
    
    console.log('DB discounts/vouchers:', db.discounts, db.vouchers);
    console.log('DB config:', dbCfg.discounts, dbCfg.vouchers);
    console.log('Static config:', statCfg.discounts, statCfg.vouchers);

    // Handle legacy: if top-level discounts/vouchers exist as strings, migrate to configuration
    if (Array.isArray(db.discounts) && db.discounts.length > 0 && (!dbCfg.discounts || dbCfg.discounts.length === 0)) {
      const statDiscountById = {};
      if (statCfg.discounts) {
        statCfg.discounts.forEach(d => { if (d && d.id) statDiscountById[String(d.id)] = d; });
      }
      if (typeof db.discounts[0] === 'string') {
        dbCfg.discounts = db.discounts.map(s => statDiscountById[String(s)] || { id: s, name: '', type: 'percentage', value: 0 });
      } else {
        dbCfg.discounts = db.discounts;
      }
    }

    if (Array.isArray(db.vouchers) && db.vouchers.length > 0 && (!dbCfg.vouchers || dbCfg.vouchers.length === 0)) {
      const statVouchByCode = {};
      if (statCfg.vouchers) {
        statCfg.vouchers.forEach(v => { if (v && v.validCodes) statVouchByCode[String(v.validCodes)] = v; });
      }
      if (typeof db.vouchers[0] === 'string') {
        dbCfg.vouchers = db.vouchers.map(s => statVouchByCode[String(s)] || { validCodes: s, type: 'percentage', value: 0 });
      } else {
        dbCfg.vouchers = db.vouchers;
      }
    }

    // If DB configuration still empty, use static data
    if (Array.isArray(dbCfg.discounts) && dbCfg.discounts.length === 0 && statCfg.discounts && statCfg.discounts.length > 0) {
      dbCfg.discounts = statCfg.discounts;
    } else if (Array.isArray(dbCfg.discounts) && dbCfg.discounts.length > 0 && typeof dbCfg.discounts[0] === 'string') {
      // Map string IDs to full objects from static
      const statDiscountById = {};
      if (statCfg.discounts) {
        statCfg.discounts.forEach(d => { if (d && d.id) statDiscountById[String(d.id)] = d; });
      }
      dbCfg.discounts = dbCfg.discounts.map(s => statDiscountById[String(s)] || { id: s, name: '', type: 'percentage', value: 0 });
    }

    if (Array.isArray(dbCfg.vouchers) && dbCfg.vouchers.length === 0 && statCfg.vouchers && statCfg.vouchers.length > 0) {
      dbCfg.vouchers = statCfg.vouchers;
    } else if (Array.isArray(dbCfg.vouchers) && dbCfg.vouchers.length > 0 && typeof dbCfg.vouchers[0] === 'string') {
      // Map string codes to full objects from static
      const statVouchByCode = {};
      if (statCfg.vouchers) {
        statCfg.vouchers.forEach(v => { if (v && v.validCodes) statVouchByCode[String(v.validCodes)] = v; });
      }
      dbCfg.vouchers = dbCfg.vouchers.map(s => statVouchByCode[String(s)] || { validCodes: s, type: 'percentage', value: 0 });
    }

    return Object.assign({}, db, { configuration: dbCfg });
  };

  const mappedDb = mapDbPrimitivesToObjects(dbBooking, staticBooking);
  const merged = staticBooking ? deepMerge(staticBooking, mappedDb || {}) : (mappedDb || null);
  
  // Clean up: remove top-level discounts/vouchers after migrating to configuration
  if (merged) {
    delete merged.discounts;
    delete merged.vouchers;
  }
  
  return merged;
};