import { gancioClient } from './gancio.client';
import { siteSettingsService } from '../modules/settings/settings.service';
import { env } from '../config/env';
import { logger } from '../utils/logger';

// CML settings fields that trigger a Gancio sync when changed
const GANCIO_RELEVANT_FIELDS = [
  'organizationName',
  'organizationShortName',
  'publicColorPrimary',
  'publicColorBgBase',
  'publicColorBgContainer',
  'publicHeaderGradient',
  'footerText',
  'enableEvents',
  // Feature flags that affect the injected nav bar
  'enableInfluence',
  'enableMap',
  'enableMediaFeatures',
  'enablePayments',
  'navConfig',
] as const;

interface GancioColorPalette {
  primary?: string;
  error?: string;
  info?: string;
  success?: string;
  warning?: string;
}

/**
 * Generates custom CSS for Gancio to match CML's dark theme.
 * Targets Vuetify component classes used in Gancio's UI.
 */
function buildCustomCss(settings: {
  publicColorBgBase?: string | null;
  publicColorBgContainer?: string | null;
  publicColorPrimary?: string | null;
  publicHeaderGradient?: string | null;
}): string {
  const lines: string[] = ['/* Auto-synced from Changemaker Lite */'];

  if (settings.publicColorBgBase) {
    lines.push(`.v-application { background-color: ${settings.publicColorBgBase} !important; }`);
    lines.push(`.v-main { background-color: ${settings.publicColorBgBase} !important; }`);
  }

  if (settings.publicColorBgContainer) {
    lines.push(`.v-card { background-color: ${settings.publicColorBgContainer} !important; }`);
    lines.push(`.v-dialog .v-card { background-color: ${settings.publicColorBgContainer} !important; }`);
  }

  if (settings.publicHeaderGradient) {
    lines.push(`.v-app-bar { background: ${settings.publicHeaderGradient} !important; }`);
  } else if (settings.publicColorPrimary) {
    lines.push(`.v-app-bar { background-color: ${settings.publicColorPrimary} !important; }`);
  }

  // Push Gancio content down to make room for the injected CML nav bar (CSS fallback)
  lines.push(`.v-application--wrap, .v-application .v-main { padding-top: 56px !important; }`);

  return lines.join('\n');
}

/**
 * Compact inline SVG icons matching Ant Design's outlined style.
 * Stroke-based, 24x24 viewBox, rendered at 1em (14px in the nav context).
 * Using fill="none" stroke="currentColor" so they inherit link color + hover transitions.
 */
const NAV_ICONS: Record<string, string> = {
  Home: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>',
  Campaigns: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>',
  Map: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>',
  Shifts: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>',
  Events: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>',
  Gallery: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><circle cx="12" cy="12" r="10"/><polygon points="10 8 16 12 10 16 10 8"/></svg>',
  Donate: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>',
  Admin: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>',
  Website: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>',
  Docs: '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>',
};

/**
 * Generates a self-contained JS snippet that injects a CML navigation bar
 * into Gancio's page. Uses Gancio's `custom_js` setting.
 *
 * The bar mirrors PublicLayout's header: same gradient, colors, nav links,
 * inline SVG icons, and feature-flag-aware conditional rendering.
 */
interface NavConfigItem {
  id: string;
  label: string;
  path: string;
  icon: string;
  enabled: boolean;
  order: number;
  type: 'builtin' | 'custom' | 'group';
  featureFlag?: string;
  external?: boolean;
  children?: NavConfigItem[];
}

/** Map navConfig icon IDs to the SVG NAV_ICONS keys */
const ICON_ID_TO_KEY: Record<string, string> = {
  HomeOutlined: 'Home',
  SendOutlined: 'Campaigns',
  EnvironmentOutlined: 'Map',
  CalendarOutlined: 'Shifts',
  ScheduleOutlined: 'Shifts',
  BarChartOutlined: 'Events',
  PlayCircleOutlined: 'Gallery',
  HeartOutlined: 'Donate',
  DollarOutlined: 'Donate',
  ShoppingOutlined: 'Donate',
  GlobalOutlined: 'Website',
  BookOutlined: 'Docs',
  TagOutlined: 'Events',
  VideoCameraOutlined: 'Events',
  FileTextOutlined: 'Docs',
  TrophyOutlined: 'Home',
};

function buildCustomJs(settings: {
  organizationName?: string | null;
  publicHeaderGradient?: string | null;
  publicColorPrimary?: string | null;
  enableInfluence?: boolean | null;
  enableMap?: boolean | null;
  enableMediaFeatures?: boolean | null;
  enablePayments?: boolean | null;
  enableEvents?: boolean | null;
  enableMeetingPlanner?: boolean | null;
  enableTicketedEvents?: boolean | null;
  enableSocial?: boolean | null;
  enableMeet?: boolean | null;
  enableLandingPages?: boolean | null;
  navConfig?: { items: NavConfigItem[] } | null;
}, appUrl: string, homeUrl: string): string {
  const orgName = settings.organizationName || 'Changemaker Lite';
  const gradient = settings.publicHeaderGradient || 'linear-gradient(135deg, #005a9c 0%, #007acc 100%)';
  // Strip trailing slash from URLs for consistent link construction
  const baseUrl = appUrl.replace(/\/+$/, '');
  const home = homeUrl.replace(/\/+$/, '');

  // Build nav links array from navConfig if available, else fall back to feature flags
  const links: Array<{ href: string; label: string; icon: string; active?: boolean }> = [];

  if (settings.navConfig?.items) {
    const featureFlagMap: Record<string, boolean | null | undefined> = {
      enableInfluence: settings.enableInfluence,
      enableMap: settings.enableMap,
      enableMediaFeatures: settings.enableMediaFeatures,
      enablePayments: settings.enablePayments,
      enableEvents: settings.enableEvents,
      enableMeetingPlanner: settings.enableMeetingPlanner,
      enableTicketedEvents: settings.enableTicketedEvents,
      enableSocial: settings.enableSocial,
      enableMeet: settings.enableMeet,
      enableLandingPages: settings.enableLandingPages,
    };

    // Flatten groups: Gancio uses a flat HTML nav bar, no dropdown support
    const flatItems: NavConfigItem[] = [];
    for (const item of settings.navConfig.items) {
      if (item.type === 'group' && item.children) {
        for (const child of item.children) flatItems.push(child);
      } else {
        flatItems.push(item);
      }
    }

    const sorted = flatItems
      .filter(item => item.enabled)
      .filter(item => !item.featureFlag || featureFlagMap[item.featureFlag] !== false)
      .sort((a, b) => a.order - b.order);

    for (const item of sorted) {
      // Resolve $token paths first
      if (item.path === '$landing') {
        links.push({ href: home, label: item.label, icon: ICON_ID_TO_KEY[item.icon] || 'Website' });
      } else if (item.path === '$docs') {
        links.push({ href: `${home}/docs/`, label: item.label, icon: ICON_ID_TO_KEY[item.icon] || 'Docs' });
      } else if (item.id === 'events') {
        // Events is the current page on Gancio, mark active
        links.push({ href: '#', label: item.label, icon: ICON_ID_TO_KEY[item.icon] || 'Events', active: true });
      } else if (item.external && item.id === 'home') {
        links.push({ href: home, label: item.label, icon: ICON_ID_TO_KEY[item.icon] || 'Home' });
      } else if (item.external) {
        links.push({ href: item.path, label: item.label, icon: ICON_ID_TO_KEY[item.icon] || item.label });
      } else {
        links.push({ href: `${baseUrl}${item.path}`, label: item.label, icon: ICON_ID_TO_KEY[item.icon] || item.label });
      }
    }
  } else {
    // Legacy fallback: build from feature flags
    links.push({ href: home, label: 'Home', icon: 'Home' });
    if (settings.enableInfluence !== false) {
      links.push({ href: `${baseUrl}/campaigns`, label: 'Campaigns', icon: 'Campaigns' });
    }
    if (settings.enableMap !== false) {
      links.push({ href: `${baseUrl}/map`, label: 'Map', icon: 'Map' });
      links.push({ href: `${baseUrl}/shifts`, label: 'Shifts', icon: 'Shifts' });
    }
    links.push({ href: '#', label: 'Events', icon: 'Events', active: true });
    if (settings.enableMediaFeatures !== false) {
      links.push({ href: `${baseUrl}/gallery`, label: 'Gallery', icon: 'Gallery' });
    }
    if (settings.enablePayments === true) {
      links.push({ href: `${baseUrl}/donate`, label: 'Donate', icon: 'Donate' });
    }
  }

  // Always add Admin link — the admin page handles its own auth guard
  links.push({ href: `${baseUrl}/app`, label: 'Admin', icon: 'Admin' });

  // Serialize links for embedding in JS (icon key is a string, resolved at runtime)
  const linksJson = JSON.stringify(links);

  // Build the icons object for the generated JS (keyed by label)
  const iconsUsed = [...new Set(links.map((l) => l.icon))];
  const iconsObj = iconsUsed
    .map((key) => `${key}:'${NAV_ICONS[key]?.replace(/'/g, "\\'") ?? ''}'`)
    .join(',');

  // Safe-escaped org name for embedding in JS string literals
  const orgNameEscaped = orgName.replace(/'/g, "\\'").replace(/</g, '&lt;').replace(/>/g, '&gt;');

  return `(function(){
  if(document.getElementById('cml-nav-bar'))return;
  var icons={${iconsObj}};
  var bar=document.createElement('div');
  bar.id='cml-nav-bar';
  bar.style.cssText='position:fixed;top:0;left:0;right:0;z-index:9999;height:56px;display:flex;align-items:center;justify-content:space-between;padding:0 24px;background:${gradient};font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;box-sizing:border-box;';
  var brand=document.createElement('a');
  brand.href='${baseUrl}/campaigns';
  brand.style.cssText='color:#fff;text-decoration:none;font-size:18px;font-weight:600;display:flex;align-items:center;gap:8px;white-space:nowrap;';
  brand.textContent='${orgNameEscaped}';
  bar.appendChild(brand);
  var nav=document.createElement('div');
  nav.className='cml-nav-links';
  nav.style.cssText='display:flex;align-items:center;gap:16px;';
  var links=${linksJson};
  links.forEach(function(l){
    var a=document.createElement('a');
    a.href=l.href;
    a.style.cssText='color:'+(l.active?'#fff':'rgba(255,255,255,0.85)')+';text-decoration:none;font-size:14px;font-weight:'+(l.active?'600':'400')+';border-bottom:'+(l.active?'2px solid #fff':'2px solid transparent')+';padding-bottom:2px;transition:color 0.2s;white-space:nowrap;display:inline-flex;align-items:center;gap:6px;';
    a.innerHTML=(icons[l.icon]||'')+'<span>'+l.label+'</span>';
    if(!l.active){
      a.addEventListener('mouseenter',function(){this.style.color='#fff';});
      a.addEventListener('mouseleave',function(){this.style.color='rgba(255,255,255,0.85)';});
    }
    nav.appendChild(a);
  });
  bar.appendChild(nav);
  var mobileBack=document.createElement('a');
  mobileBack.href='${baseUrl}/campaigns';
  mobileBack.className='cml-nav-mobile-back';
  mobileBack.style.cssText='display:none;color:#fff;text-decoration:none;font-size:14px;white-space:nowrap;';
  mobileBack.textContent='\\u2190 Back to ${orgNameEscaped}';
  bar.appendChild(mobileBack);
  var style=document.createElement('style');
  style.textContent='.v-application--wrap,.v-application .v-main{padding-top:56px!important;}@media(max-width:768px){.cml-nav-links{display:none!important;}.cml-nav-mobile-back{display:block!important;}}';
  document.head.appendChild(style);
  document.body.insertBefore(bar,document.body.firstChild);
})();`;
}

class GancioSettingsSyncService {
  private syncInProgress = false;

  /**
   * Quick check: does the update payload contain any Gancio-relevant fields?
   */
  hasGancioChanges(payload: Record<string, unknown>): boolean {
    return GANCIO_RELEVANT_FIELDS.some((f) => f in payload);
  }

  /**
   * Full sync: reads CML settings, reads existing Gancio settings (to preserve
   * non-primary colors), then pushes all mapped settings to Gancio.
   * Used at API startup.
   */
  async syncAll(): Promise<void> {
    if (!gancioClient.enabled) return;

    if (this.syncInProgress) {
      logger.debug('Gancio settings sync already in progress, skipping');
      return;
    }

    this.syncInProgress = true;
    try {
      const settings = await siteSettingsService.get();

      // Guard: check DB-level feature flag
      if (!settings.enableEvents) {
        logger.debug('Gancio sync skipped: enableEvents is false');
        return;
      }

      // Read existing Gancio settings to preserve non-primary color values
      const gancioSettings = await gancioClient.getSettings();

      const promises: Promise<boolean>[] = [];

      // Title & description
      if (settings.organizationName) {
        promises.push(gancioClient.setSetting('title', settings.organizationName));
        promises.push(gancioClient.setSetting('description', `${settings.organizationName} Events`));
      }

      // Dark theme
      promises.push(gancioClient.setSetting('theme.is_dark', true));

      // Color palettes — read-then-merge to preserve error/info/success/warning
      if (settings.publicColorPrimary) {
        const existingDark = (gancioSettings?.dark_colors as GancioColorPalette) || {};
        const existingLight = (gancioSettings?.light_colors as GancioColorPalette) || {};

        promises.push(gancioClient.setSetting('dark_colors', {
          ...existingDark,
          primary: settings.publicColorPrimary,
        }));
        promises.push(gancioClient.setSetting('light_colors', {
          ...existingLight,
          primary: settings.publicColorPrimary,
        }));
      }

      // Footer links
      if (settings.footerText) {
        const adminUrl = env.ADMIN_URL;
        promises.push(gancioClient.setSetting('footerLinks', [
          { label: settings.footerText, href: adminUrl },
        ]));
      }

      // Custom CSS for deeper theming
      const css = buildCustomCss(settings);
      promises.push(gancioClient.setSetting('custom_css', css));

      // Custom JS — inject CML navigation bar into Gancio
      const appUrl = env.ADMIN_URL;
      const homeUrl = `https://${env.DOMAIN}`;
      const js = buildCustomJs(settings as any, appUrl, homeUrl);
      promises.push(gancioClient.setSetting('custom_js', js));

      const results = await Promise.allSettled(promises);
      const succeeded = results.filter((r) => r.status === 'fulfilled' && r.value).length;
      const failed = results.length - succeeded;

      if (failed > 0) {
        logger.warn(`Gancio settings sync: ${succeeded} succeeded, ${failed} failed`);
      } else {
        logger.info(`Gancio settings sync: synced ${succeeded} settings`);
      }
    } catch (err) {
      logger.warn('Gancio settings sync failed:', err instanceof Error ? err.message : err);
    } finally {
      this.syncInProgress = false;
    }
  }

  /**
   * Partial sync: only pushes Gancio settings for fields that actually changed.
   * Used from the PUT /api/settings handler.
   */
  async syncChanged(changedFields: Record<string, unknown>): Promise<void> {
    if (!gancioClient.enabled) return;

    try {
      // If enableEvents was just turned off, skip sync
      if (changedFields.enableEvents === false) {
        logger.debug('Gancio sync skipped: enableEvents set to false');
        return;
      }

      // If enableEvents was just turned on, do a full sync
      if (changedFields.enableEvents === true) {
        await this.syncAll();
        return;
      }

      // Check DB-level feature flag (may not be in changedFields)
      const settings = await siteSettingsService.get();
      if (!settings.enableEvents) {
        logger.debug('Gancio sync skipped: enableEvents is false');
        return;
      }

      const promises: Promise<boolean>[] = [];

      // Organization name → title + description
      if ('organizationName' in changedFields && changedFields.organizationName) {
        const name = changedFields.organizationName as string;
        promises.push(gancioClient.setSetting('title', name));
        promises.push(gancioClient.setSetting('description', `${name} Events`));
      }

      // Primary color → dark_colors + light_colors (read-then-merge)
      if ('publicColorPrimary' in changedFields && changedFields.publicColorPrimary) {
        const gancioSettings = await gancioClient.getSettings();
        const existingDark = (gancioSettings?.dark_colors as GancioColorPalette) || {};
        const existingLight = (gancioSettings?.light_colors as GancioColorPalette) || {};

        promises.push(gancioClient.setSetting('dark_colors', {
          ...existingDark,
          primary: changedFields.publicColorPrimary,
        }));
        promises.push(gancioClient.setSetting('light_colors', {
          ...existingLight,
          primary: changedFields.publicColorPrimary,
        }));
      }

      // Footer text → footerLinks
      if ('footerText' in changedFields) {
        const adminUrl = env.ADMIN_URL;
        if (changedFields.footerText) {
          promises.push(gancioClient.setSetting('footerLinks', [
            { label: changedFields.footerText as string, href: adminUrl },
          ]));
        } else {
          promises.push(gancioClient.setSetting('footerLinks', []));
        }
      }

      // Theme-related CSS fields — rebuild CSS from full settings (not just changed fields)
      const cssFields = ['publicColorBgBase', 'publicColorBgContainer', 'publicHeaderGradient', 'publicColorPrimary'];
      if (cssFields.some((f) => f in changedFields)) {
        const css = buildCustomCss(settings);
        promises.push(gancioClient.setSetting('custom_css', css));
      }

      // Nav bar JS — rebuild when org name, feature flags, header gradient, or navConfig change
      const jsFields = [
        'organizationName', 'publicHeaderGradient', 'publicColorPrimary',
        'enableInfluence', 'enableMap', 'enableMediaFeatures', 'enablePayments',
        'navConfig',
      ];
      if (jsFields.some((f) => f in changedFields)) {
        const appUrl = env.ADMIN_URL;
        const homeUrl = `https://${env.DOMAIN}`;
        const js = buildCustomJs(settings as any, appUrl, homeUrl);
        promises.push(gancioClient.setSetting('custom_js', js));
      }

      if (promises.length === 0) return;

      const results = await Promise.allSettled(promises);
      const succeeded = results.filter((r) => r.status === 'fulfilled' && r.value).length;
      const failed = results.length - succeeded;

      if (failed > 0) {
        logger.warn(`Gancio partial sync: ${succeeded}/${results.length} succeeded`);
      } else {
        logger.debug(`Gancio partial sync: ${succeeded} settings updated`);
      }
    } catch (err) {
      logger.warn('Gancio partial sync failed:', err instanceof Error ? err.message : err);
    }
  }
}

export const gancioSettingsSyncService = new GancioSettingsSyncService();
