/**
 * Website Theme Overrides
 *
 * Applies the user's manual customisation (colours + CTA behaviour) on top of a
 * generated website WITHOUT regenerating it. The theme is compiled into a single
 * <style> block plus an optional <script> block that are injected into every
 * stored .html page, so the customisation survives page refreshes, ZIP downloads
 * and publishing.
 *
 * Mirrors `services/landingPageThemeOverrides.ts` in shape, but the selectors
 * target what the website generator actually emits: there is no lead-capture
 * form, no `.bg-light` contrast safety net and no `.btn-outline-accent`, and the
 * generated stylesheet resolves nearly everything through the `--bg-*`/`--text-*`
 * variables. See the frontend twin for the full reasoning.
 *
 * IMPORTANT: `buildThemeOverrideCss` / `buildCtaBehaviorScript` are mirrored in
 * `src/frontend/src/modules/content/website-planner/utils/websiteThemeCustomization.ts`,
 * which drives the instant in-preview rendering. Keep the two in sync.
 */

// ============================================
// TYPES
// ============================================

export type CtaActionMode = 'default' | 'link';

export interface WebsiteCtaAction {
  mode: CtaActionMode;
  url: string;
  openInNewTab: boolean;
}

export interface WebsiteThemeCustomization {
  pageBackground: string | null;
  sectionBackground: string | null;
  cardBackground: string | null;
  headingColor: string | null;
  bodyTextColor: string | null;
  mutedTextColor: string | null;
  primaryColor: string | null;
  accentColor: string | null;
  borderColor: string | null;
  ctaBackground: string | null;
  ctaTextColor: string | null;
  ctaAction: WebsiteCtaAction;
}

export const DEFAULT_CTA_ACTION: WebsiteCtaAction = {
  mode: 'default',
  url: '',
  openInNewTab: true,
};

export const DEFAULT_THEME: WebsiteThemeCustomization = {
  pageBackground: null,
  sectionBackground: null,
  cardBackground: null,
  headingColor: null,
  bodyTextColor: null,
  mutedTextColor: null,
  primaryColor: null,
  accentColor: null,
  borderColor: null,
  ctaBackground: null,
  ctaTextColor: null,
  ctaAction: { ...DEFAULT_CTA_ACTION },
};

export const THEME_STYLE_ID = 'mengo-theme-overrides';
export const CTA_SCRIPT_ID = 'mengo-cta-behavior';

/** Selector matching every element that should trigger the CTA action. */
export const CTA_TRIGGER_SELECTOR = '.btn-primary,.nav-cta,[data-cta]';

// ============================================
// SANITISATION
// ============================================

const HEX_RE = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;

/**
 * Only plain hex colours are accepted. Everything written into the page's CSS
 * goes through here, so a client cannot smuggle arbitrary CSS into the stored
 * HTML by way of a "colour" value.
 */
function safeColor(value: unknown): string | null {
  if (typeof value !== 'string') return null;
  const trimmed = value.trim();
  return HEX_RE.test(trimmed) ? trimmed.toLowerCase() : null;
}

function hexToRgbTriplet(hex: string): string {
  let h = hex.replace('#', '');
  if (h.length === 3) h = h.split('').map(c => c + c).join('');
  return `${parseInt(h.slice(0, 2), 16)}, ${parseInt(h.slice(2, 4), 16)}, ${parseInt(h.slice(4, 6), 16)}`;
}

/** Bare domains get https://; anchors, absolute paths, mailto: and tel: pass through. */
export function normalizeCtaUrl(url: unknown): string {
  const trimmed = typeof url === 'string' ? url.trim() : '';
  if (!trimmed) return '';
  if (/^(https?:\/\/|mailto:|tel:|#|\/)/i.test(trimmed)) return trimmed;
  return `https://${trimmed}`;
}

/** Blocks javascript:/data: and anything else that isn't a plain navigation target. */
export function isSafeCtaUrl(url: unknown): boolean {
  const value = normalizeCtaUrl(url);
  if (!value) return false;
  return /^(https?:\/\/[^\s]+|mailto:[^\s]+|tel:[^\s]+|#[^\s]*|\/[^\s]*)$/i.test(value);
}

/** Coerce a request/DB payload into a valid, safe theme object. */
export function normalizeTheme(raw: any): WebsiteThemeCustomization {
  const source = raw && typeof raw === 'object' ? raw : {};
  const rawCta = source.ctaAction && typeof source.ctaAction === 'object' ? source.ctaAction : {};
  const url = typeof rawCta.url === 'string' ? rawCta.url.trim().slice(0, 2048) : '';
  return {
    pageBackground: safeColor(source.pageBackground),
    sectionBackground: safeColor(source.sectionBackground),
    cardBackground: safeColor(source.cardBackground),
    headingColor: safeColor(source.headingColor),
    bodyTextColor: safeColor(source.bodyTextColor),
    mutedTextColor: safeColor(source.mutedTextColor),
    primaryColor: safeColor(source.primaryColor),
    accentColor: safeColor(source.accentColor),
    borderColor: safeColor(source.borderColor),
    ctaBackground: safeColor(source.ctaBackground),
    ctaTextColor: safeColor(source.ctaTextColor),
    ctaAction: {
      mode: rawCta.mode === 'link' ? 'link' : 'default',
      url: isSafeCtaUrl(url) ? url : '',
      openInNewTab: rawCta.openInNewTab !== false,
    },
  };
}

/** True when the theme leaves the generated website exactly as the AI produced it. */
export function isThemeEmpty(theme: WebsiteThemeCustomization): boolean {
  const colorsUntouched = ([
    'pageBackground', 'sectionBackground', 'cardBackground',
    'headingColor', 'bodyTextColor', 'mutedTextColor',
    'primaryColor', 'accentColor', 'borderColor',
    'ctaBackground', 'ctaTextColor',
  ] as const).every(key => !theme[key]);
  return colorsUntouched && theme.ctaAction.mode !== 'link';
}

// ============================================
// CSS BUILDER
// ============================================

/**
 * Compile the theme into a CSS override block.
 *
 * Repeating the `:root` pseudo-class stacks specificity without changing what a
 * selector matches, which is how these overrides beat the generated stylesheet.
 */
export function buildThemeOverrideCss(rawTheme: any): string {
  const theme = normalizeTheme(rawTheme);
  // Nothing customised — leave the generated website exactly as the AI produced it.
  if (isThemeEmpty(theme)) return '';

  const rules: string[] = [];
  const rootVars: string[] = [];

  const {
    pageBackground, sectionBackground, cardBackground,
    headingColor, bodyTextColor, mutedTextColor,
    primaryColor, accentColor, borderColor,
    ctaBackground, ctaTextColor,
  } = theme;

  if (pageBackground) rootVars.push(`--bg-dark:${pageBackground}`);
  if (sectionBackground) rootVars.push(`--bg-section:${sectionBackground}`);
  if (cardBackground) rootVars.push(`--bg-card:${cardBackground}`, `--glass-bg:${cardBackground}`);
  if (headingColor) rootVars.push(`--text-primary:${headingColor}`);
  if (bodyTextColor) rootVars.push(`--text-secondary:${bodyTextColor}`);
  if (mutedTextColor) rootVars.push(`--text-muted:${mutedTextColor}`);
  if (primaryColor) rootVars.push(`--primary:${primaryColor}`);
  if (accentColor) rootVars.push(`--accent:${accentColor}`, `--secondary:${accentColor}`);
  if (borderColor) rootVars.push(`--border:${borderColor}`, `--glass-border:${borderColor}`);
  if (primaryColor || accentColor) {
    rootVars.push('--gradient-primary:linear-gradient(135deg,var(--primary),var(--accent))');
  }
  if (primaryColor) {
    rootVars.push(`--shadow-glow:0 0 30px rgba(${hexToRgbTriplet(primaryColor)},.3)`);
  }
  if (rootVars.length > 0) rules.push(`:root{${rootVars.join(';')}}`);

  // `body` hardcodes its background and colour, so the variables never reach it.
  if (pageBackground) {
    rules.push(`:root:root body{background:${pageBackground} !important}`);
    rules.push(`:root:root :is(.hero,.footer,.navbar,.nav,.mobile-menu,.bg-dark,.bg-default){background:${pageBackground} !important}`);
  }
  if (sectionBackground) {
    rules.push(`:root:root :is(.section-alt,.bg-solid,.bg-glass){background:${sectionBackground} !important}`);
  }
  if (cardBackground) {
    rules.push(
      `:root:root :is(.card,.testimonial-card,.pricing-card,.team-card,.feature-card,.stat-item){background:${cardBackground} !important}`,
    );
  }

  // h4–h6 never receive `color: var(--text-primary)` from the generated CSS.
  if (headingColor) {
    rules.push(`:root:root:root :is(h1,h2,h3,h4,h5,h6):not(.gradient-text){color:${headingColor} !important}`);
  }
  if (bodyTextColor) {
    rules.push(`:root:root body{color:${bodyTextColor} !important}`);
    rules.push(`:root:root:root :is(p,li,blockquote,figcaption,dd,dt,td,th){color:${bodyTextColor} !important}`);
  }
  if (mutedTextColor) {
    rules.push(`:root:root :is(.footer-bottom,.stat-label,.badge-muted,.text-muted){color:${mutedTextColor} !important}`);
  }
  if (borderColor) {
    rules.push(`:root:root :is(.card,.testimonial-card,.pricing-card,.team-card,.feature-card,.stat-item,.btn-secondary,input,select,textarea){border-color:${borderColor} !important}`);
  }

  // `.btn-primary` hardcodes its label colour alongside the accent background.
  if (ctaBackground) {
    const rgb = hexToRgbTriplet(ctaBackground);
    rules.push(`:root:root :is(.btn-primary,.nav-cta){background:${ctaBackground} !important;box-shadow:0 4px 14px rgba(${rgb},.4)}`);
    rules.push(`:root:root :is(.btn-primary,.nav-cta):hover{box-shadow:0 6px 20px rgba(${rgb},.5)}`);
    rules.push(`:root:root .btn-secondary:hover{border-color:${ctaBackground} !important;color:${ctaBackground} !important}`);
  }
  if (ctaTextColor) {
    rules.push(`:root:root:root :is(.btn-primary,.btn-primary:hover,.nav-cta,.nav-cta:hover){color:${ctaTextColor} !important}`);
  }

  return rules.join('\n');
}

// ============================================
// CTA BEHAVIOUR SCRIPT
// ============================================

/**
 * Build the <script> block that redirects CTA clicks to a custom link.
 * Returns '' for the default mode, where the page keeps its own behaviour.
 */
export function buildCtaBehaviorScript(cta: WebsiteCtaAction): string {
  if (!cta || cta.mode !== 'link' || !isSafeCtaUrl(cta.url)) return '';

  const config = JSON.stringify({
    url: normalizeCtaUrl(cta.url),
    newTab: cta.openInNewTab !== false,
  }).replace(/</g, '\\u003c');

  // window.__MENGO_CTA_DISABLED lets the preview switch this off and drive the
  // CTA from the parent instead, so a click never fires both handlers.
  return `<script id="${CTA_SCRIPT_ID}">
(function(){
  var CFG=${config};
  if(!CFG.url) return;
  var SEL=${JSON.stringify(CTA_TRIGGER_SELECTOR)};
  function go(){ if(CFG.newTab){ window.open(CFG.url,'_blank','noopener'); } else { window.location.href=CFG.url; } }
  document.addEventListener('click',function(e){
    if(window.__MENGO_CTA_DISABLED) return;
    var el=e.target;
    if(!el||typeof el.closest!=='function') return;
    var trigger=el.closest(SEL);
    if(!trigger) return;
    e.preventDefault(); e.stopPropagation(); go();
  },true);
})();
</script>`;
}

// ============================================
// HTML INJECTION
// ============================================

const STYLE_BLOCK_RE = new RegExp(`\\s*<style[^>]*id=["']${THEME_STYLE_ID}["'][\\s\\S]*?<\\/style>`, 'gi');
const SCRIPT_BLOCK_RE = new RegExp(`\\s*<script[^>]*id=["']${CTA_SCRIPT_ID}["'][\\s\\S]*?<\\/script>`, 'gi');

/**
 * Remove any previously injected override blocks.
 * Also used before handing existing HTML to the AI for regeneration, so the
 * overrides are never baked into the newly generated markup.
 */
export function stripThemeOverrides(html: string): string {
  if (!html) return html;
  return html.replace(STYLE_BLOCK_RE, '').replace(SCRIPT_BLOCK_RE, '');
}

/**
 * The stylesheet the in-app page editor compiles from each section's alignment
 * choice (`src/frontend/.../landing-pages/wizard/utils/pageEditor.ts`, shared by
 * both generators). Stripped and re-applied around a save so an edit never
 * stacks duplicate blocks.
 */
export const LAYOUT_STYLE_ID = 'mengo-layout-overrides';
const LAYOUT_BLOCK_RE = new RegExp(`\\s*<style[^>]*id=["']${LAYOUT_STYLE_ID}["'][\\s\\S]*?<\\/style>`, 'gi');

/** Drop the editor's per-section layout stylesheet. */
export function stripLayoutOverrides(html: string): string {
  if (!html) return html;
  return html.replace(LAYOUT_BLOCK_RE, '');
}

/**
 * Inject the theme override blocks into one generated page.
 * Existing blocks are replaced, so this is safe to call repeatedly.
 */
export function applyThemeToHtml(html: string, rawTheme: any): string {
  if (!html) return html;

  const theme = normalizeTheme(rawTheme);
  const output = stripThemeOverrides(html);

  if (isThemeEmpty(theme)) return output;

  const blocks: string[] = [];
  const css = buildThemeOverrideCss(theme);
  // Guard against a stray "</style>" ever closing the block early.
  if (css) blocks.push(`<style id="${THEME_STYLE_ID}">\n${css.replace(/<\/style/gi, '<\\/style')}\n</style>`);
  const script = buildCtaBehaviorScript(theme.ctaAction);
  if (script) blocks.push(script);

  if (blocks.length === 0) return output;

  const block = `\n${blocks.join('\n')}\n`;
  if (/<\/body>/i.test(output)) {
    // Last thing before </body> — after every generated <style> and <script>.
    // Function replacement so a "$" in the CTA URL is never treated as a pattern.
    return output.replace(/<\/body>/i, () => `${block}</body>`);
  }
  return output + block;
}
