import { z } from 'zod';
import type { Tool } from './types';

/**
 * theme — produce a `--kai-*` CSS token override block from a brand color or
 * description. Tokens are sourced exclusively from the real names in theme.css.
 *
 * Real --kai-color-* names (verified against repo-root theme.css):
 *   --kai-color-primary            → brand color
 *   --kai-color-primary-foreground → AA-contrast fg (white or black)
 *   --kai-color-ring               → focus ring (brand color)
 *   --kai-color-accent             → tinted accent surface
 *   --kai-color-accent-foreground  → text on accent surface
 */

// ─── hex color utilities (no deps) ──────────────────────────────────────────

/** Parse #rgb or #rrggbb → { r, g, b } 0-255. Returns null on failure. */
function parseHex(hex: string): { r: number; g: number; b: number } | null {
  const s = hex.trim().replace(/^#/, '');
  if (s.length === 3) {
    const r = parseInt(s[0] + s[0], 16);
    const g = parseInt(s[1] + s[1], 16);
    const b = parseInt(s[2] + s[2], 16);
    if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
    return { r, g, b };
  }
  if (s.length === 6) {
    const r = parseInt(s.slice(0, 2), 16);
    const g = parseInt(s.slice(2, 4), 16);
    const b = parseInt(s.slice(4, 6), 16);
    if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
    return { r, g, b };
  }
  return null;
}

function toHex(r: number, g: number, b: number): string {
  return (
    '#' +
    [r, g, b]
      .map((v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, '0'))
      .join('')
  );
}

/**
 * Relative luminance (WCAG 2.1) — used for AA contrast check.
 * Returns 0 (black) to 1 (white).
 */
function relativeLuminance(r: number, g: number, b: number): number {
  const lin = (c: number) => {
    const s = c / 255;
    return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
  };
  return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
}

/** White or black for maximum contrast against the brand color. */
function foreground(r: number, g: number, b: number): '#ffffff' | '#000000' {
  const lum = relativeLuminance(r, g, b);
  // Contrast against white = (1 + 0.05) / (lum + 0.05)
  // Contrast against black = (lum + 0.05) / (0 + 0.05)
  const contrastWhite = 1.05 / (lum + 0.05);
  const contrastBlack = (lum + 0.05) / 0.05;
  return contrastWhite >= contrastBlack ? '#ffffff' : '#000000';
}

/** Lighten a color by a ratio 0-1 (moves toward white). */
function lighten(r: number, g: number, b: number, ratio: number): string {
  return toHex(
    r + (255 - r) * ratio,
    g + (255 - g) * ratio,
    b + (255 - b) * ratio,
  );
}

/** Darken a color by a ratio 0-1 (moves toward black). */
function darken(r: number, g: number, b: number, ratio: number): string {
  return toHex(r * (1 - ratio), g * (1 - ratio), b * (1 - ratio));
}

/** Produce a tinted accent surface (very light tint of the brand color). */
function accentSurface(r: number, g: number, b: number): string {
  return lighten(r, g, b, 0.88);
}

/** Dark-mode variant: lighten the brand so it reads well on dark backgrounds. */
function darkModeBrand(r: number, g: number, b: number): string {
  const lum = relativeLuminance(r, g, b);
  // If brand is already light enough for dark bg, keep it; otherwise lighten.
  if (lum > 0.25) return toHex(r, g, b);
  return lighten(r, g, b, 0.35);
}

/** Dark-mode accent surface: subtle dark tint of brand. */
function darkAccentSurface(r: number, g: number, b: number): string {
  return darken(r, g, b, 0.75);
}

// ─── keyword → hex fallback map ──────────────────────────────────────────────

const KEYWORD_COLORS: Record<string, string> = {
  blue:    '#3b82f6',
  navy:    '#1e3a8a',
  sky:     '#0ea5e9',
  cyan:    '#06b6d4',
  teal:    '#14b8a6',
  green:   '#22c55e',
  emerald: '#10b981',
  lime:    '#84cc16',
  yellow:  '#eab308',
  amber:   '#f59e0b',
  orange:  '#f97316',
  red:     '#ef4444',
  rose:    '#f43f5e',
  pink:    '#ec4899',
  purple:  '#a855f7',
  violet:  '#7c3aed',
  indigo:  '#6366f1',
  slate:   '#64748b',
  gray:    '#6b7280',
  zinc:    '#71717a',
  neutral: '#737373',
  stone:   '#78716c',
  white:   '#f8fafc',
  black:   '#0f172a',
};

const DEFAULT_BRAND = '#6366f1'; // indigo — kit's own brand feel

/**
 * Resolve a brand hex from input.
 * Returns { hex, note } where note explains the assumption if we guessed.
 */
function resolveBrand(
  brand?: string,
  description?: string,
): { hex: string; r: number; g: number; b: number; note: string } {
  // 1. Explicit hex
  if (brand) {
    const parsed = parseHex(brand);
    if (parsed) {
      return { hex: brand.startsWith('#') ? brand : `#${brand}`, ...parsed, note: '' };
    }
    // brand provided but unparseable — fall through with a note
    const fallback = parseHex(DEFAULT_BRAND)!;
    return {
      hex: DEFAULT_BRAND,
      ...fallback,
      note: `Could not parse \`${brand}\` as a hex color — defaulted to **${DEFAULT_BRAND}** (indigo). Pass a valid \`#rgb\` or \`#rrggbb\` value to use your color.`,
    };
  }

  // 2. Description → keyword match
  if (description) {
    const lower = description.toLowerCase();
    for (const [keyword, hex] of Object.entries(KEYWORD_COLORS)) {
      if (lower.includes(keyword)) {
        const parsed = parseHex(hex)!;
        return {
          hex,
          ...parsed,
          note: `No brand color provided — inferred **${hex}** (${keyword}) from description. Override with \`brand: '#rrggbb'\` to use an exact color.`,
        };
      }
    }
    // Description present but no keyword matched → tasteful default
    const parsed = parseHex(DEFAULT_BRAND)!;
    return {
      hex: DEFAULT_BRAND,
      ...parsed,
      note: `No recognizable color keyword in description — defaulted to **${DEFAULT_BRAND}** (indigo). Override with \`brand: '#rrggbb'\` for an exact color.`,
    };
  }

  // 3. Nothing provided → kit brand default
  const parsed = parseHex(DEFAULT_BRAND)!;
  return {
    hex: DEFAULT_BRAND,
    ...parsed,
    note: `No brand color or description provided — defaulted to **${DEFAULT_BRAND}** (indigo, the kit's own brand). Pass \`brand: '#rrggbb'\` to use your color.`,
  };
}

// ─── CSS block builders ───────────────────────────────────────────────────────

interface TokenSet {
  primary: string;
  primaryFg: string;
  ring: string;
  accent: string;
  accentFg: string;
}

function buildTokenSet(r: number, g: number, b: number): TokenSet {
  const primary = toHex(r, g, b);
  const primaryFg = foreground(r, g, b);
  const accent = accentSurface(r, g, b);
  // foreground on the very-light accent surface
  const accentFgColor = foreground(
    ...([accent]
      .map((h) => parseHex(h)!)
      .flatMap((c) => [c.r, c.g, c.b]) as [number, number, number]),
  );
  return {
    primary,
    primaryFg,
    ring: primary,
    accent,
    accentFg: accentFgColor,
  };
}

function buildDarkTokenSet(r: number, g: number, b: number): TokenSet {
  const darkBrand = darkModeBrand(r, g, b);
  const parsed = parseHex(darkBrand)!;
  const primary = darkBrand;
  const primaryFg = foreground(parsed.r, parsed.g, parsed.b);
  const accent = darkAccentSurface(r, g, b);
  const accentFgParsed = parseHex(accent)!;
  return {
    primary,
    primaryFg,
    ring: darkBrand,
    accent,
    accentFg: foreground(accentFgParsed.r, accentFgParsed.g, accentFgParsed.b),
  };
}

function cssBlock(selector: string, tokens: TokenSet): string {
  return [
    `${selector} {`,
    `  --kai-color-primary:             ${tokens.primary};`,
    `  --kai-color-primary-foreground:  ${tokens.primaryFg};`,
    `  --kai-color-ring:                ${tokens.ring};`,
    `  --kai-color-accent:              ${tokens.accent};`,
    `  --kai-color-accent-foreground:   ${tokens.accentFg};`,
    `}`,
  ].join('\n');
}

// ─── Tool definition ─────────────────────────────────────────────────────────

export const theme: Tool = {
  name: 'theme',
  description:
    'Generate a --kai-* CSS token override block from a brand color or description. ' +
    'Paste the block into your global stylesheet to brand every kai-* element in one shot.',

  inputSchema: z.object({
    brand: z
      .string()
      .optional()
      .describe('Brand color as #rgb or #rrggbb hex (e.g. "#7c3aed")'),
    description: z
      .string()
      .optional()
      .describe(
        'Natural-language description of your product (used to infer a color when `brand` is omitted)',
      ),
    mode: z
      .enum(['light', 'dark', 'both'])
      .optional()
      .describe(
        '"light" → :root only (default); "dark" → .dark only; "both" → :root + .dark',
      ),
  }),

  handler: async (args) => {
    const {
      brand,
      description,
      mode = 'light',
    } = args as { brand?: string; description?: string; mode?: 'light' | 'dark' | 'both' };

    const { hex, r, g, b, note } = resolveBrand(brand, description);

    const lightTokens = buildTokenSet(r, g, b);
    const darkTokens = buildDarkTokenSet(r, g, b);

    // Which blocks to emit
    const blocks: string[] = [];
    if (mode === 'light' || mode === 'both') {
      blocks.push(cssBlock(':root', lightTokens));
    }
    if (mode === 'dark' || mode === 'both') {
      blocks.push(cssBlock('.dark', darkTokens));
    }

    const cssOutput = blocks.join('\n\n');

    const noteLabel = note.startsWith('Could not parse') ? 'Note' : 'Assumption';
    const noteSection = note ? `\n> **${noteLabel}:** ${note}\n` : '';

    const text = `\
## AI/UI theme override — \`${hex}\`
${noteSection}
Paste this block into your **global stylesheet** (before the \`@import\` of \`@kitn.ai/ui\` or after — order doesn't matter since \`--kai-*\` tokens are resolved at runtime via \`var()\` fallbacks). For \`mode:'dark'\` overrides, the \`.dark\` class must be on a parent element (e.g. \`<html class="dark">\`) for them to take effect.

\`\`\`css
${cssOutput}
\`\`\`

### How it works

The \`--kai-color-*\` tokens pierce the Shadow DOM via CSS custom-property inheritance. Every \`kai-*\` element reads them through a \`var(--kai-color-primary, <default>)\` fallback chain defined in \`theme.css\`. Setting them on \`:root\` (or a wrapper element) is enough to rebrand everything globally.

**Tokens emitted** (verified names from \`theme.css\`):
- \`--kai-color-primary\` — primary brand color
- \`--kai-color-primary-foreground\` — AA-contrast foreground on primary (auto-selected black/white)
- \`--kai-color-ring\` — keyboard focus ring
- \`--kai-color-accent\` — tinted accent surface
- \`--kai-color-accent-foreground\` — text on accent surface

### Apply to a subtree only

\`\`\`css
/* scope to a specific container instead of :root */
.my-chat-widget {
${blocks[0]
    ?.split('\n')
    .slice(1, -1)
    .map((l) => '  ' + l)
    .join('\n')}
}
\`\`\`

### Fine-tune the full palette

The theme editor lets you tweak every token interactively and copy the result:

**→ [Open Theme Editor](https://ui.kitn.ai/theme/editor/)**

For the complete token list see the [theming guide](https://ui.kitn.ai/guides/theming/) and the source \`theme.css\` in the package root.
`;

    return { content: [{ type: 'text', text }] };
  },
};
