import { LitElement, html, nothing } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { getCssText } from './nile-color-picker.css';
import '../nile-dropdown/nile-dropdown';
import '../nile-menu/nile-menu';
import '../nile-menu-item/nile-menu-item';

export type ColorPickerType = 'swatches' | 'picker';
export type ColorPickerMode = 'inline' | 'popover';
export type SwatchTooltip = 'name' | 'value' | 'both';
export type PaletteEntry = { color: string; name: string };

export const DEFAULT_PALETTE: PaletteEntry[] = [
  { color: '#131316', name: 'Black' },
  { color: '#26272B', name: 'Dark Gray' },
  { color: '#525252', name: 'Gray' },
  { color: '#737373', name: 'Medium Gray' },
  { color: '#A3A3A3', name: 'Silver' },
  { color: '#D6D6D6', name: 'Light Gray' },
  { color: '#F5F5F5', name: 'Off White' },
  { color: '#FFFFFF', name: 'White' },
  { color: '#CA8504', name: 'Yellow 3' },
  { color: '#C01048', name: 'Red 3' },
  { color: '#DD2590', name: 'Pink 3' },
  { color: '#155EEF', name: 'Blue Dark 3' },
  { color: '#444CE7', name: 'Indigo 3' },
  { color: '#7A5AF8', name: 'Purple 3' },
  { color: '#3E4784', name: 'Gray Blue 3' },
  { color: '#087443', name: 'Green 3' },
  { color: '#FDE272', name: 'Yellow 2' },
  { color: '#FEA3B4', name: 'Red 2' },
  { color: '#FAA7E0', name: 'Pink 2' },
  { color: '#528BFF', name: 'Blue Dark 2' },
  { color: '#A4BCFD', name: 'Indigo 2' },
  { color: '#BDB4FE', name: 'Purple 2' },
  { color: '#B3B8DB', name: 'Gray Blue 2' },
  { color: '#73E2A3', name: 'Green 2' },
  { color: '#FEFBE8', name: 'Yellow 1' },
  { color: '#FFF1F3', name: 'Red 1' },
  { color: '#FDF2FA', name: 'Pink 1' },
  { color: '#EFF4FF', name: 'Blue Dark 1' },
  { color: '#EEF4FF', name: 'Indigo 1' },
  { color: '#F4F3FF', name: 'Purple 1' },
  { color: '#F8F9FC', name: 'Gray Blue 1' },
  { color: '#EDFCF2', name: 'Green 1' },
];

/* ── color math helpers ──────────────────────────────────────────────── */
export function hexToRgb(hex: string): [number, number, number] {
  const h = hex.replace('#', '');
  return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
}

export function rgbToHex(r: number, g: number, b: number): string {
  return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('').toUpperCase();
}

export function rgbToHsv(r: number, g: number, b: number): [number, number, number] {
  r /= 255; g /= 255; b /= 255;
  const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min;
  let h = 0;
  if (d !== 0) {
    if (max === r) h = ((g - b) / d + 6) % 6;
    else if (max === g) h = (b - r) / d + 2;
    else h = (r - g) / d + 4;
    h *= 60;
  }
  return [h, max === 0 ? 0 : d / max, max];
}

export function hsvToRgb(h: number, s: number, v: number): [number, number, number] {
  const c = v * s, x = c * (1 - Math.abs(((h / 60) % 2) - 1)), m = v - c;
  let r = 0, g = 0, b = 0;
  if (h < 60)       { r = c; g = x; }
  else if (h < 120) { r = x; g = c; }
  else if (h < 180) { g = c; b = x; }
  else if (h < 240) { g = x; b = c; }
  else if (h < 300) { r = x; b = c; }
  else              { r = c; b = x; }
  return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255)];
}

@customElement('nile-color-picker')
export class NileColorPicker extends LitElement {
  protected createRenderRoot() { return this; }

 
  @property({ type: String, attribute: true, reflect: true }) type: ColorPickerType = 'swatches';

  /** Currently selected hex color value. */
  @property({ type: String, attribute: true, reflect: true }) value = '#000000';

  /** What to show in swatch tooltips. */
  @property({ type: String, reflect: true }) swatchTooltip: SwatchTooltip = 'name';

  /** How many recent/custom colors to display. */
  @property({ type: Number, reflect: true }) recentColorsCount = 3;

  /** Show a "No Fill" button (useful for background-color use cases). */
  @property({ type: Boolean, reflect: true }) noFill = false;

 
  @property({ type: String, reflect: true })mode: ColorPickerMode = 'inline';

  /** Show the SV gradient canvas (the large colour area). Applies when type="picker". */
  @property({ type: Boolean,   reflect: true }) showCanvas = true;

  /** Show the hue slider row (rainbow bar + eyedropper). Applies when type="picker". */
  @property({ type: Boolean,  reflect: true }) showHue = true;

  /** Show the HEX / RGB input row. Applies when type="picker". */
  @property({ type: Boolean, reflect: true }) showInputs = true;

  /** Show the Cancel / Okay action buttons. Applies when type="picker". */
  @property({ type: Boolean,  reflect: true }) showActions = true;

  @property({ type: Boolean, reflect: true, attribute: true}) open = false;

  /** Custom palette. Accepts a JSON array of `{ color, name }` objects or plain
   *  hex strings. Falls back to the built-in palette when empty. */
  @property({
    attribute: 'palette',
    converter: {
      fromAttribute: (value: string): PaletteEntry[] => {
        try {
          const parsed = JSON.parse(value);
          if (!Array.isArray(parsed)) return [];
          return parsed.map((e: any) => {
            if (typeof e === 'string') return { color: e, name: e };
            if (e && typeof e.color === 'string') return { color: e.color, name: e.name || e.color };
            return null;
          }).filter(Boolean) as PaletteEntry[];
        } catch { return []; }
      },
      toAttribute: (v: PaletteEntry[]) => JSON.stringify(v),
    },
  })
  palette: PaletteEntry[] = [];

  /* ── internal state ──────────────────────────────────────────────── */
  @state() private _view: ColorPickerType = 'swatches';
  @state() private _recentColors: PaletteEntry[] = [];

  // picker canvas state
  @state() private _hue = 220;
  @state() private _sat = 0.7;
  @state() private _val = 0.93;
  @state() private _colorMode: 'HEX' | 'RGB' = 'HEX';
  @state() private _hexInput = '#366AEE';
  @state() private _r = 54;
  @state() private _g = 106;
  @state() private _b = 238;

  private _svCanvas: HTMLCanvasElement | null = null;
  private _hueCanvas: HTMLCanvasElement | null = null;
  private _draggingSV = false;
  private _draggingHue = false;
  private _pendingColor = '#366AEE';

  /* ── lifecycle ───────────────────────────────────────────────────── */
  connectedCallback() {
    super.connectedCallback();
    this._view = this.type;
    if (this.type === 'picker') {
      const [r, g, b] = hexToRgb(this._norm(this.value));
      [this._hue, this._sat, this._val] = rgbToHsv(r, g, b);
      this._syncInputs();
    }
    this._injectCss();
    document.addEventListener('mousemove', this._onGlobalMouseMove);
    document.addEventListener('mouseup', this._onGlobalMouseUp);
  }

  disconnectedCallback() {
    document.removeEventListener('mousemove', this._onGlobalMouseMove);
    document.removeEventListener('mouseup', this._onGlobalMouseUp);
    super.disconnectedCallback();
  }

  protected updated(changed: Map<string, unknown>) {
    if (changed.has('type')) {
      this._view = this.type;
      if (this.type === 'picker') {
        const [r, g, b] = hexToRgb(this._norm(this.value));
        [this._hue, this._sat, this._val] = rgbToHsv(r, g, b);
        this._syncInputs();
      }
    }
    if (changed.has('value') && this.type === 'picker' && this._view === 'picker' &&
        !this._draggingSV && !this._draggingHue) {
      const [r, g, b] = hexToRgb(this._norm(this.value));
      [this._hue, this._sat, this._val] = rgbToHsv(r, g, b);
      this._syncInputs();
    }
 
    if (this._view === 'picker') {
      requestAnimationFrame(() => {
        this._svCanvas = this.querySelector('.ncp-sv-canvas') as HTMLCanvasElement;
        this._hueCanvas = this.querySelector('.ncp-hue-canvas') as HTMLCanvasElement;
        this._drawSV();
        this._drawHue();
      });
    }
  }

  /* ── public API ──────────────────────────────────────────────────── */
  /** Reset the view back to the default type (call this when the host popover closes). */
  reset() {
    this._view = this.type;
  }

 
  private _injectCss() {
    if (this.querySelector('style[data-nile-color-picker]')) return;
    const style = document.createElement('style');
    style.setAttribute('data-nile-color-picker', '');
    style.textContent = getCssText();
    this.insertBefore(style, this.firstChild);
  }

 
  private _norm(hex: string) {
    const t = hex.trim();
    if (/^#[0-9a-f]{6}$/i.test(t)) return t.toUpperCase();
    if (/^#[0-9a-f]{3}$/i.test(t)) {
      const [a, b, c] = t.slice(1).toUpperCase().split('');
      return `#${a}${a}${b}${b}${c}${c}`;
    }
    return '#000000';
  }

  private get _paletteColors(): PaletteEntry[] {
    return this.palette.length > 0 ? this.palette : DEFAULT_PALETTE;
  }

 
  private _emit(value: string) {
    this.dispatchEvent(new CustomEvent('nile-change', {
      detail: { value },
      bubbles: true,
      composed: true,
    }));
  }

  private _apply(color: string) {
    const hex = this._norm(color);
    this.value = hex;
    const entry: PaletteEntry = { color: hex, name: hex };
    this._recentColors = [entry, ...this._recentColors.filter(e => this._norm(e.color) !== hex)].slice(0, this.recentColorsCount);
    this._emit(hex);
    if (this.mode === 'popover' && this._view === 'swatches') {
      this._closePopover();
    }
  }

  private _closePopover() {
    const pop = this.querySelector('nile-popover') as any;
    if (pop) pop.isShow = false;
  }

  private _openPicker() {
    const [r, g, b] = hexToRgb(this._norm(this.value));
    const [h, s, v] = rgbToHsv(r, g, b);
    this._hue = h; this._sat = s; this._val = v;
    this._syncInputs();
    this._view = 'picker';
  }

  private _syncInputs() {
    const [r, g, b] = hsvToRgb(this._hue, this._sat, this._val);
    const hex = rgbToHex(r, g, b);
    this._hexInput = hex; this._r = r; this._g = g; this._b = b;
    this._pendingColor = hex;
  }

  private _drawSV() {
    const canvas = this._svCanvas;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    if (!ctx) return;
    const { width: w, height: h } = canvas;
    const [hr, hg, hb] = hsvToRgb(this._hue, 1, 1);
    ctx.fillStyle = `rgb(${hr},${hg},${hb})`;
    ctx.fillRect(0, 0, w, h);
    const wg = ctx.createLinearGradient(0, 0, w, 0);
    wg.addColorStop(0, 'rgba(255,255,255,1)'); wg.addColorStop(1, 'rgba(255,255,255,0)');
    ctx.fillStyle = wg; ctx.fillRect(0, 0, w, h);
    const bg = ctx.createLinearGradient(0, 0, 0, h);
    bg.addColorStop(0, 'rgba(0,0,0,0)'); bg.addColorStop(1, 'rgba(0,0,0,1)');
    ctx.fillStyle = bg; ctx.fillRect(0, 0, w, h);
  }

  private _drawHue() {
    const canvas = this._hueCanvas;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    if (!ctx) return;
    const grad = ctx.createLinearGradient(0, 0, canvas.width, 0);
    ['#FF0000','#FFFF00','#00FF00','#00FFFF','#0000FF','#FF00FF','#FF0000']
      .forEach((c, i, a) => grad.addColorStop(i / (a.length - 1), c));
    ctx.fillStyle = grad; ctx.fillRect(0, 0, canvas.width, canvas.height);
  }

  private _onSVMouseDown = (e: MouseEvent) => { e.preventDefault(); this._draggingSV = true; this._updateSV(e); };
  private _onHueMouseDown = (e: MouseEvent) => { e.preventDefault(); this._draggingHue = true; this._updateHue(e); };

  private _onGlobalMouseMove = (e: MouseEvent) => {
    if (this._draggingSV) this._updateSV(e);
    if (this._draggingHue) this._updateHue(e);
  };
  private _onGlobalMouseUp = () => {
    const wasDraggingSV = this._draggingSV;
    const wasDraggingHue = this._draggingHue;
    if (!this.showActions && this._view === 'picker' && (wasDraggingSV || wasDraggingHue)) {
      this._commitPickerIfNoActions();
    }
    this._draggingSV = false;
    this._draggingHue = false;
  };

  private _commitPickerIfNoActions() {
    if (this.showActions) return;
    this._apply(this._pendingColor);
    if (this.mode === 'popover') {
      if (this.type === 'swatches') this._view = 'swatches';
      this._closePopover();
    }
  }

  private _updateSV(e: MouseEvent) {
    const canvas = this._svCanvas; if (!canvas) return;
    const rect = canvas.getBoundingClientRect();
    this._sat = Math.max(0, Math.min(e.clientX - rect.left, rect.width)) / rect.width;
    this._val = 1 - Math.max(0, Math.min(e.clientY - rect.top, rect.height)) / rect.height;
    this._syncInputs();
  }

  private _updateHue(e: MouseEvent) {
    const canvas = this._hueCanvas; if (!canvas) return;
    const rect = canvas.getBoundingClientRect();
    this._hue = (Math.max(0, Math.min(e.clientX - rect.left, rect.width)) / rect.width) * 360;
    this._syncInputs();
  }

  private _onHexInput = (e: Event) => {
    let val = ((e as CustomEvent).detail?.value ?? (e.target as HTMLInputElement).value ?? '').trim();
    if (!val.startsWith('#')) val = '#' + val;
    if (/^#[0-9a-f]{6}$/i.test(val)) {
      const [r, g, b] = hexToRgb(val);
      [this._hue, this._sat, this._val] = rgbToHsv(r, g, b);
      this._syncInputs();
      if (this._view === 'picker') this._commitPickerIfNoActions();
    }
  };

  private _onRgbInput = (ch: 'r' | 'g' | 'b', e: Event) => {
    const raw = (e as CustomEvent).detail?.value ?? (e.target as HTMLInputElement).value;
    const v = Math.max(0, Math.min(255, parseInt(String(raw), 10) || 0));
    if (ch === 'r') this._r = v; else if (ch === 'g') this._g = v; else this._b = v;
    [this._hue, this._sat, this._val] = rgbToHsv(this._r, this._g, this._b);
    this._syncInputs();
    if (this._view === 'picker') this._commitPickerIfNoActions();
  };

  private _onColorModeSelect = (e: CustomEvent<{ value: string }>) => {
    e.stopPropagation();
    const v = e.detail?.value;
    if (v === 'HEX' || v === 'RGB') {
      this._colorMode = v;
    }
  };

  private _pickEyedropper = async () => {
    const Ctor = (window as any).EyeDropper;
    if (!Ctor) { this._openPicker(); return; }
    try {
      const result = await new Ctor().open();
      if (result?.sRGBHex) this._apply(result.sRGBHex);
    } catch { /* cancelled */ }
  };
  private _renderSwatch(entry: PaletteEntry) {
    const norm = this._norm(entry.color);
    const isActive = this._norm(this.value) === norm;
    const tip = this.swatchTooltip === 'name'
      ? (entry.name || norm)
      : this.swatchTooltip === 'value'
        ? norm
        : entry.name ? `${entry.name} (${norm})` : norm;
    return html`
      <nile-lite-tooltip content=${tip}>
        <nile-color-swatch
          color=${norm}
          ?active=${isActive}
          @click=${() => this._apply(norm)}
        ></nile-color-swatch>
      </nile-lite-tooltip>
    `;
  }

  private _renderSwatchesView() {
    const recent = this._recentColors.slice(0, this.recentColorsCount);
    return html`
      <div class="ncp-panel">
        <div class="ncp-palette-grid">
          ${this._paletteColors.map(e => this._renderSwatch(e))}
        </div>

        ${this.noFill ? html`
          <button class="ncp-no-fill" @click=${() => this._emit('none')}>
           <nile-glyph name="disabled"></nile-glyph>
            <span>No Fill</span>
          </button>
        ` : nothing}

        <div class="ncp-custom-section">
          <p class="ncp-custom-title">Custom</p>
          <div class="ncp-custom-row">
            ${recent.map(e => this._renderSwatch(e))}
            <nile-lite-tooltip content="Add custom color">
              <button class="ncp-action-btn" @click=${() => this._openPicker()}>
<nile-glyph name="plus"></nile-glyph>
              </button>
            </nile-lite-tooltip>
            <nile-lite-tooltip content="Pick from screen">
              <button class="ncp-action-btn" @click=${this._pickEyedropper}>
                <nile-glyph name="colorize" size="12"></nile-glyph>
              </button>
            </nile-lite-tooltip>
          </div>
        </div>
      </div>
    `;
  }

  private _renderPickerView() {
    const svX = this._sat * 100;
    const svY = (1 - this._val) * 100;
    const hueX = (this._hue / 360) * 100;

    return html`
      <div class="ncp-panel ncp-panel--picker">

        ${this.showCanvas ? html`
          <div class="ncp-sv-area" @mousedown=${this._onSVMouseDown}>
            <canvas class="ncp-sv-canvas" width="244" height="130"></canvas>
            <div class="ncp-sv-thumb" style="left:${svX}%;top:${svY}%">
              <div class="ncp-sv-thumb-inner"></div>
            </div>
          </div>
        ` : nothing}

        ${this.showHue ? html`
          <div class="ncp-hue-row">
            <div class="ncp-hue-wrap" @mousedown=${this._onHueMouseDown}>
              <canvas class="ncp-hue-canvas" width="244" height="14"></canvas>
              <div class="ncp-hue-thumb" style="left:${hueX}%"></div>
            </div>
            <button class="ncp-eyedropper" title="Pick from screen" @click=${this._pickEyedropper}>
              <nile-glyph name="colorize" size="16"></nile-glyph>
            </button>
          </div>
        ` : nothing}

        ${this.showInputs ? html`
          <div class="ncp-input-row">
            <nile-dropdown class="ncp-mode-dropdown" placement="bottom-start" distance="4" hoist>
              <nile-button slot="trigger" class="ncp-mode-btn" variant="tertiary">
                ${this._colorMode} <nile-icon name="arrowdown" size="12"></nile-icon>
              </nile-button>
              <nile-menu @nile-select=${this._onColorModeSelect}>
                <nile-menu-item value="HEX" ?active=${this._colorMode === 'HEX'}>HEX</nile-menu-item>
                <nile-menu-item value="RGB" ?active=${this._colorMode === 'RGB'}>RGB</nile-menu-item>
              </nile-menu>
            </nile-dropdown>
            ${this._colorMode === 'HEX'
              ? html`<nile-input class="ncp-hex" .value=${this._hexInput} @nile-input=${this._onHexInput}></nile-input>`
              : html`
                  <nile-input class="ncp-rgb" type="number" min="0" max="255" .value=${String(this._r)} @nile-input=${(e: Event) => this._onRgbInput('r', e)}></nile-input>
                  <nile-input class="ncp-rgb" type="number" min="0" max="255" .value=${String(this._g)} @nile-input=${(e: Event) => this._onRgbInput('g', e)}></nile-input>
                  <nile-input class="ncp-rgb" type="number" min="0" max="255" .value=${String(this._b)} @nile-input=${(e: Event) => this._onRgbInput('b', e)}></nile-input>
                `}
          </div>
        ` : nothing}

        ${this.showActions ? html`
          <div class="ncp-picker-actions">
            ${this.type === 'swatches'
              ? html`<nile-button variant="secondary" size="small" @click=${() => { this._view = 'swatches'; }}>Cancel</nile-button>`
              : nothing}
            <nile-button variant="primary" size="small" @click=${() => { this._apply(this._pendingColor); if (this.type === 'swatches') this._view = 'swatches'; this._closePopover(); }}>Okay</nile-button>
          </div>
        ` : nothing}

      </div>
    `;
  }

  private _renderTrigger() {
    const isNoFill = this.value === 'none';
    const displayColor = isNoFill ? null : this._norm(this.value);
    return html`
      <button
        slot="anchor"
        class="ncp-trigger${isNoFill ? ' ncp-trigger--no-fill' : ''}"
        type="button"
        aria-label=${isNoFill ? 'No Fill' : displayColor}
        style=${isNoFill ? '' : `background:${displayColor}`}
      ></button>
    `;
  }

  render() {
    const panel = this._view === 'picker' ? this._renderPickerView() : this._renderSwatchesView();

    if (this.mode === 'popover') {
      return html`
        <nile-popover
          .arrow=${false}
          flip
          .open=${this.open}
          placement="bottom-start"
          id="color-picker-popover"
          distance="8"
          @nile-hide=${() => this.reset()}
        >
          ${this._renderTrigger()}
          ${panel}
        </nile-popover>
      `;
    }

    return panel;
  }
}


declare global {
  interface HTMLElementTagNameMap {
    'nile-color-picker': NileColorPicker;
  }
}
