export const THEME_COLOURS = [
  ['bg', 'Background'], ['bg-soft', 'Soft background'], ['panel', 'Panels'],
  ['panel-2', 'Raised panels'], ['rail-bg', 'Spaces rail'], ['border', 'Borders'],
  ['text', 'Text'], ['muted', 'Muted text'], ['accent', 'Accent'],
  ['accent-2', 'Secondary accent'], ['on-accent', 'Text on accent'], ['glow', 'Glow'],
  ['hover', 'Hover'], ['hover-faint', 'Subtle hover'],
  ['selection-bg', 'Selection background'], ['selection-text', 'Selected text'],
  ['ok', 'Success'], ['ok-text', 'Success text'],
  ['danger', 'Error'], ['danger-bg', 'Error background'],
  ['danger-border', 'Error border'], ['danger-text', 'Error text'],
  ['warn', 'Warning'], ['warn-bg', 'Warning background'],
  ['warn-border', 'Warning border'], ['warn-text', 'Warning text']
] as const;

export type ThemeColour = (typeof THEME_COLOURS)[number][0];
export type ThemeColours = Record<ThemeColour, string>;
export interface ThemeDocument {
  format: 'malice-theme';
  version: 1;
  name: string;
  scheme: 'dark' | 'light';
  colours: ThemeColours;
  palette: string[];
  nameLightness: number;
}
export interface CustomTheme extends ThemeDocument {
  id: `custom:${string}`;
}

export const MALICIOUS_PALETTE = [
  '#1b2021', '#cecbca', '#272a2a', '#202e2f', '#c43325', '#c46056',
  '#8cc992', '#c2dab0', '#ffb852', '#ffab5b', '#5299ff', '#92beff',
  '#645ac9', '#928cc9', '#5abfc9', '#8cc4c9', '#b0c2da', '#caccce'
];

export function isThemeColour(value: unknown): value is `#${string}` {
  return typeof value === 'string' && /^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/i.test(value);
}

export function isThemeDocument(value: unknown): value is ThemeDocument {
  if (typeof value !== 'object' || value === null) return false;
  const doc = value as Record<string, unknown>;
  return doc.format === 'malice-theme' && doc.version === 1 &&
    typeof doc.name === 'string' && doc.name.trim().length > 0 && doc.name.length <= 80 &&
    (doc.scheme === 'dark' || doc.scheme === 'light') &&
    typeof doc.nameLightness === 'number' && Number.isFinite(doc.nameLightness) &&
    doc.nameLightness >= 0 && doc.nameLightness <= 100 &&
    Array.isArray(doc.palette) && doc.palette.length <= 64 && doc.palette.every(isThemeColour) &&
    typeof doc.colours === 'object' && doc.colours !== null &&
    THEME_COLOURS.every(([key]) => isThemeColour((doc.colours as Record<string, unknown>)[key]));
}

export function isCustomTheme(value: unknown): value is CustomTheme {
  return isThemeDocument(value) && 'id' in value &&
    typeof value.id === 'string' && /^custom:[\w-]{1,80}$/.test(value.id);
}

export function themeDocument(value: ThemeDocument): ThemeDocument {
  return {
    format: 'malice-theme', version: 1, name: value.name.trim(), scheme: value.scheme,
    colours: Object.fromEntries(THEME_COLOURS.map(([key]) => [key, value.colours[key]])) as ThemeColours,
    palette: [...value.palette], nameLightness: value.nameLightness
  };
}

export function readThemeFile(contents: string): ThemeDocument {
  let value: unknown;
  try {
    value = JSON.parse(contents);
  } catch {
    throw new Error('This file does not contain valid JSON.');
  }
  if (!isThemeDocument(value)) throw new Error('Choose a valid Malice theme file with version 1.');
  return themeDocument(value);
}

function colourHex(value: string): string {
  if (/^#[0-9a-f]{3,4}$/i.test(value)) return '#' + [...value.slice(1)].map((char) => char + char).join('');
  if (isThemeColour(value)) return value;
  const channels = value.match(/[\d.]+/g)?.map(Number);
  if (!channels || channels.length < 3) throw new Error('Could not read the current theme colours.');
  const bytes = channels.slice(0, 3);
  if (channels.length === 4) bytes.push(Math.round(channels[3] * 255));
  return '#' + bytes.map((byte) => Math.round(byte).toString(16).padStart(2, '0')).join('');
}

export function captureTheme(name: string, palette?: string[]): ThemeDocument {
  const style = getComputedStyle(document.documentElement);
  const colours = Object.fromEntries(THEME_COLOURS.map(([key]) => [key, colourHex(style.getPropertyValue(`--${key}`).trim())])) as ThemeColours;
  return {
    format: 'malice-theme', version: 1, name,
    scheme: style.colorScheme === 'light' ? 'light' : 'dark', colours,
    palette: palette ? [...palette] : [...new Set(Object.values(colours))].slice(0, 64),
    nameLightness: parseFloat(style.getPropertyValue('--name-lightness'))
  };
}

export function themeStyle(doc: ThemeDocument): string {
  return THEME_COLOURS.map(([key]) => `--${key}:${doc.colours[key]}`).join(';') +
    `;--name-lightness:${doc.nameLightness}%;color-scheme:${doc.scheme}`;
}
