import { useQuery } from '@tanstack/react-query';
import { useEffect } from 'react';

export interface Branding {
  appName: string;
  primaryColor: string;
  loginPageTitle: string;
  logoUrl: string | null;
  faviconUrl: string | null;
  footerText: string | null;
}

const DEFAULT_BRANDING: Branding = {
  appName: 'MAESTRO',
  primaryColor: '#2563eb',
  loginPageTitle: 'MAESTRO',
  logoUrl: null,
  faviconUrl: null,
  footerText: null,
};

function toNullableString(v: unknown): string | null {
  return typeof v === 'string' && v.length > 0 ? v : null;
}

async function fetchBranding(): Promise<Branding> {
  try {
    const res = await fetch('/api/branding');
    if (!res.ok) return DEFAULT_BRANDING;
    const data = await res.json();
    return {
      appName: typeof data.appName === 'string' ? data.appName : DEFAULT_BRANDING.appName,
      primaryColor: typeof data.primaryColor === 'string' ? data.primaryColor : DEFAULT_BRANDING.primaryColor,
      loginPageTitle: typeof data.loginPageTitle === 'string' ? data.loginPageTitle : DEFAULT_BRANDING.loginPageTitle,
      logoUrl: toNullableString(data.logoUrl),
      faviconUrl: toNullableString(data.faviconUrl),
      footerText: toNullableString(data.footerText),
    };
  } catch {
    return DEFAULT_BRANDING;
  }
}

function applyFavicon(url: string | null): void {
  const href = url ?? `${import.meta.env.BASE_URL}favicon.svg`;
  let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]');
  if (!link) {
    link = document.createElement('link');
    link.rel = 'icon';
    document.head.appendChild(link);
  }
  if (link.href !== href) link.href = href;
}

interface Rgb { r: number; g: number; b: number }

function parseColor(input: string): Rgb | null {
  const s = input.trim();
  const hex = s.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
  if (hex) {
    const h = hex[1];
    const full = h.length === 3 ? h.split('').map(c => c + c).join('') : h;
    return {
      r: parseInt(full.slice(0, 2), 16),
      g: parseInt(full.slice(2, 4), 16),
      b: parseInt(full.slice(4, 6), 16),
    };
  }
  const rgb = s.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
  if (rgb) {
    return { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]) };
  }
  return null;
}

function rgbToHsl({ r, g, b }: Rgb): { h: number; s: number; l: number } {
  const rn = r / 255, gn = g / 255, bn = b / 255;
  const max = Math.max(rn, gn, bn), min = Math.min(rn, gn, bn);
  const l = (max + min) / 2;
  let h = 0, s = 0;
  if (max !== min) {
    const d = max - min;
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
    switch (max) {
      case rn: h = ((gn - bn) / d + (gn < bn ? 6 : 0)); break;
      case gn: h = ((bn - rn) / d + 2); break;
      case bn: h = ((rn - gn) / d + 4); break;
    }
    h *= 60;
  }
  return { h, s, l };
}

function hslToCss(h: number, s: number, l: number): string {
  return `hsl(${h.toFixed(1)} ${(s * 100).toFixed(1)}% ${(l * 100).toFixed(1)}%)`;
}

/**
 * primaryColor から派生カラー（ホバー用 deep / 選択背景 soft / focus リング / text on brand）を計算して
 * document.documentElement の CSS 変数に反映する。
 */
function applyBrandColors(primary: string): void {
  const root = document.documentElement;
  const rgb = parseColor(primary);
  // unparsable: keep the raw value for --brand-primary, skip derivatives so fallbacks apply.
  if (!rgb) {
    root.style.setProperty('--brand-primary', primary);
    root.style.removeProperty('--brand-primary-deep');
    root.style.removeProperty('--brand-primary-soft');
    root.style.removeProperty('--brand-primary-ring');
    root.style.removeProperty('--brand-primary-fg');
    return;
  }

  const { h, s, l } = rgbToHsl(rgb);
  // Hover: 12% darker, clamp
  const deepL = Math.max(0.05, l - 0.12);
  // Soft background: very light but keep some hue. Lower saturation so it stays neutral-ish.
  const softL = Math.min(0.96, Math.max(0.92, 1 - l * 0.05));
  const softS = Math.min(s, 0.55);
  // Luminance for contrast decision (BT.601).
  const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
  const fg = luminance > 0.65 ? '#0f172a' : '#ffffff';

  root.style.setProperty('--brand-primary', primary);
  root.style.setProperty('--brand-primary-deep', hslToCss(h, s, deepL));
  root.style.setProperty('--brand-primary-soft', hslToCss(h, softS, softL));
  root.style.setProperty('--brand-primary-ring', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.35)`);
  root.style.setProperty('--brand-primary-fg', fg);
}

/**
 * Branding 情報を取得し、document.title, --brand-primary CSS 変数, favicon に反映する。
 * 値は /api/branding から取得。config.yaml の branding セクションが唯一の source of truth。
 */
export function useBranding(): Branding {
  const { data } = useQuery({
    queryKey: ['branding'],
    queryFn: fetchBranding,
    staleTime: 5 * 60 * 1000,
    refetchOnWindowFocus: false,
  });

  const branding = data ?? DEFAULT_BRANDING;

  useEffect(() => {
    document.title = branding.appName;
    applyBrandColors(branding.primaryColor);
    applyFavicon(branding.faviconUrl);
  }, [branding.appName, branding.primaryColor, branding.faviconUrl]);

  return branding;
}
