import {
  customElement,
  DeesElement,
  type TemplateResult,
  html,
  property,
  css,
  cssManager,
  state,
} from '@design.estate/dees-element';
import { zIndexLayers } from '../00zindex.js';
import { demo } from './eco-screensaver.demo.js';

declare global {
  interface HTMLElementTagNameMap {
    'eco-screensaver': EcoScreensaver;
  }
}

// Subtle shadcn-inspired color palette
const colors = [
  'hsl(0 0% 98%)',      // zinc-50
  'hsl(240 5% 65%)',    // zinc-400
  'hsl(240 4% 46%)',    // zinc-500
  'hsl(240 5% 34%)',    // zinc-600
  'hsl(217 91% 60%)',   // blue-500
  'hsl(142 71% 45%)',   // green-500
];

@customElement('eco-screensaver')
export class EcoScreensaver extends DeesElement {
  public static demo = demo;

  // Instance management
  public static instance: EcoScreensaver | null = null;

  public static async show(): Promise<EcoScreensaver> {
    if (EcoScreensaver.instance) {
      EcoScreensaver.instance.active = true;
      return EcoScreensaver.instance;
    }

    const screensaver = new EcoScreensaver();
    screensaver.active = true;
    document.body.appendChild(screensaver);
    EcoScreensaver.instance = screensaver;
    return screensaver;
  }

  public static hide(): void {
    if (EcoScreensaver.instance) {
      EcoScreensaver.instance.active = false;
    }
  }

  public static destroy(): void {
    if (EcoScreensaver.instance) {
      EcoScreensaver.instance.remove();
      EcoScreensaver.instance = null;
    }
  }

  // Styles
  public static styles = [
    cssManager.defaultStyles,
    css`
      :host {
        position: fixed;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
        z-index: ${zIndexLayers.overlay.screensaver};
        pointer-events: none;
        opacity: 0;
        transition: opacity 0.3s ease;
      }

      :host([active]) {
        pointer-events: all;
        opacity: 1;
      }

      .backdrop {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: hsl(240 10% 4%);
        opacity: 0;
        transition: opacity 1.5s ease 0.5s;
      }

      :host([active]) .backdrop {
        opacity: 1;
      }

      .vignette {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: radial-gradient(
          circle at center,
          transparent 0%,
          transparent 5%,
          hsla(240 10% 4% / 0.2) 20%,
          hsla(240 10% 4% / 0.6) 40%,
          hsla(240 10% 4% / 0.9) 60%,
          hsl(240 10% 4%) 80%
        );
        transform: scale(3);
        opacity: 0;
        transition:
          transform 2s cubic-bezier(0.4, 0, 0.2, 1),
          opacity 0.5s ease;
      }

      :host([active]) .vignette {
        transform: scale(1);
        opacity: 1;
      }

      /* Container for all screensaver visuals - gets masked on dismiss */
      .screensaver-content {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
      }

      .time-container {
        position: absolute;
        top: 0;
        left: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 8px;
        user-select: none;
        white-space: nowrap;
        will-change: transform;
        opacity: 0;
        transition: opacity 0.8s ease;
        transition-delay: 0s;
      }

      :host([active]) .time-container {
        opacity: 1;
        transition-delay: 1.2s;
      }

      .time {
        font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
        font-size: 96px;
        font-weight: 200;
        letter-spacing: -2px;
        line-height: 1;
        transition: color 1.5s ease;
      }

      .date {
        font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
        font-size: 18px;
        font-weight: 400;
        letter-spacing: 0.5px;
        opacity: 0.5;
        text-transform: uppercase;
        transition: color 1.5s ease;
      }

      @media (max-width: 600px) {
        .time {
          font-size: 48px;
          letter-spacing: -1px;
        }

        .date {
          font-size: 14px;
        }
      }

      .hint {
        position: fixed;
        bottom: 32px;
        left: 50%;
        transform: translateX(-50%) translateY(20px);
        padding: 12px 24px;
        background: hsl(240 6% 15%);
        border: 1px solid hsl(240 5% 26%);
        border-radius: 8px;
        color: hsl(0 0% 90%);
        font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
        font-size: 14px;
        font-weight: 500;
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.3s ease, transform 0.3s ease;
        z-index: 10;
      }

      .hint.visible {
        opacity: 1;
        transform: translateX(-50%) translateY(0);
      }
    `,
  ];

  @property({ type: Boolean, reflect: true })
  accessor active = false;

  @property({ type: Number })
  accessor delay = 0; // milliseconds before activation (0 = no delay)

  @state()
  accessor currentTime = '';

  @state()
  accessor currentDate = '';

  @state()
  accessor currentColor = colors[0];

  // Animation state - non-reactive for smooth animation
  private posX = 100;
  private posY = 100;
  private velocityX = 0.3;
  private velocityY = 0.2;
  private animationId: number | null = null;
  private timeUpdateInterval: ReturnType<typeof setInterval> | null = null;
  private colorIndex = 0;
  private elementWidth = 280;
  private elementHeight = 140;
  private hasBounced = false;
  private timeContainerEl: HTMLElement | null = null;
  private vignetteEl: HTMLElement | null = null;
  private contentEl: HTMLElement | null = null;
  private delayTimeoutId: ReturnType<typeof setTimeout> | null = null;
  private boundResetDelayTimer: () => void;
  private boundShowHint: () => void;
  private hintEl: HTMLElement | null = null;
  private hintTimeoutId: ReturnType<typeof setTimeout> | null = null;
  private hintVisible = false;

  constructor() {
    super();
    this.updateTime();
    this.boundResetDelayTimer = this.resetDelayTimer.bind(this);
    this.boundShowHint = this.showHint.bind(this);
  }

  public render(): TemplateResult {
    return html`
      <div class="screensaver-content" @click=${this.handleClick}>
        <div class="backdrop"></div>
        <div class="vignette"></div>
        <div class="time-container">
          <span class="time" style="color: ${this.currentColor};">${this.currentTime}</span>
          <span class="date" style="color: ${this.currentColor};">${this.currentDate}</span>
        </div>
      </div>
      <div class="hint">Click to exit screensaver</div>
    `;
  }

  public firstUpdated(): void {
    this.timeContainerEl = this.shadowRoot?.querySelector('.time-container') as HTMLElement;
    this.vignetteEl = this.shadowRoot?.querySelector('.vignette') as HTMLElement;
    this.contentEl = this.shadowRoot?.querySelector('.screensaver-content') as HTMLElement;
    this.hintEl = this.shadowRoot?.querySelector('.hint') as HTMLElement;
  }

  async connectedCallback(): Promise<void> {
    await super.connectedCallback();

    // If delay is set, start the delay timer and listen for activity
    if (this.delay > 0 && !this.active) {
      this.startDelayTimer();
      this.addActivityListeners();
    } else if (this.active) {
      this.startAnimation();
      this.startTimeUpdate();
    }
  }

  async disconnectedCallback(): Promise<void> {
    await super.disconnectedCallback();
    this.stopAnimation();
    this.stopTimeUpdate();
    this.stopDelayTimer();
    this.removeActivityListeners();
  }

  updated(changedProperties: Map<string, unknown>): void {
    super.updated(changedProperties);
    if (changedProperties.has('active')) {
      if (this.active) {
        // Reset mask for fresh activation
        if (this.contentEl) {
          this.contentEl.style.maskImage = '';
          this.contentEl.style.webkitMaskImage = '';
        }
        // Hide hint when freshly activated
        this.hideHint();
        // Listen for mouse movement to show hint
        window.addEventListener('mousemove', this.boundShowHint);
        this.startAnimation();
        this.startTimeUpdate();
      } else {
        window.removeEventListener('mousemove', this.boundShowHint);
        this.hideHint();
        this.stopAnimation();
        this.stopTimeUpdate();
      }
    }
  }

  private updateTime(): void {
    const now = new Date();
    const hours = String(now.getHours()).padStart(2, '0');
    const minutes = String(now.getMinutes()).padStart(2, '0');
    this.currentTime = `${hours}:${minutes}`;

    // Format date like "Monday, January 6"
    const options: Intl.DateTimeFormatOptions = {
      weekday: 'long',
      month: 'long',
      day: 'numeric',
    };
    this.currentDate = now.toLocaleDateString('en-US', options);
  }

  private startTimeUpdate(): void {
    if (this.timeUpdateInterval) return;
    this.updateTime();
    this.timeUpdateInterval = setInterval(() => this.updateTime(), 1000);
  }

  private stopTimeUpdate(): void {
    if (this.timeUpdateInterval) {
      clearInterval(this.timeUpdateInterval);
      this.timeUpdateInterval = null;
    }
  }

  private startAnimation(): void {
    if (this.animationId) return;

    // Initialize position randomly
    const maxX = window.innerWidth - this.elementWidth;
    const maxY = window.innerHeight - this.elementHeight;
    this.posX = Math.random() * Math.max(0, maxX);
    this.posY = Math.random() * Math.max(0, maxY);

    // Randomize initial direction - very slow, elegant movement
    this.velocityX = (Math.random() > 0.5 ? 1 : -1) * (0.2 + Math.random() * 0.15);
    this.velocityY = (Math.random() > 0.5 ? 1 : -1) * (0.15 + Math.random() * 0.1);

    // Reset bounce state
    this.hasBounced = false;

    const animate = () => {
      if (!this.active) {
        this.animationId = null;
        return;
      }

      const maxX = window.innerWidth - this.elementWidth;
      const maxY = window.innerHeight - this.elementHeight;

      // Update position
      this.posX += this.velocityX;
      this.posY += this.velocityY;

      // Track if we're currently at a boundary
      let atBoundary = false;

      // Bounce off walls
      if (this.posX <= 0) {
        this.posX = 0;
        this.velocityX = Math.abs(this.velocityX);
        atBoundary = true;
      } else if (this.posX >= maxX) {
        this.posX = maxX;
        this.velocityX = -Math.abs(this.velocityX);
        atBoundary = true;
      }

      if (this.posY <= 0) {
        this.posY = 0;
        this.velocityY = Math.abs(this.velocityY);
        atBoundary = true;
      } else if (this.posY >= maxY) {
        this.posY = maxY;
        this.velocityY = -Math.abs(this.velocityY);
        atBoundary = true;
      }

      // Change color only once per bounce (when entering boundary, not while at it)
      if (atBoundary && !this.hasBounced) {
        this.hasBounced = true;
        this.colorIndex = (this.colorIndex + 1) % colors.length;
        this.currentColor = colors[this.colorIndex];
      } else if (!atBoundary) {
        this.hasBounced = false;
      }

      // Direct DOM manipulation for smooth position updates (no re-render)
      if (this.timeContainerEl) {
        this.timeContainerEl.style.transform = `translate(${this.posX}px, ${this.posY}px)`;
      }

      this.animationId = requestAnimationFrame(animate);
    };

    this.animationId = requestAnimationFrame(animate);
  }

  private stopAnimation(): void {
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
      this.animationId = null;
    }
  }

  private startDelayTimer(): void {
    this.stopDelayTimer();
    this.delayTimeoutId = setTimeout(() => {
      this.removeActivityListeners();
      this.active = true;
    }, this.delay);
  }

  private stopDelayTimer(): void {
    if (this.delayTimeoutId) {
      clearTimeout(this.delayTimeoutId);
      this.delayTimeoutId = null;
    }
  }

  private resetDelayTimer(): void {
    if (this.delay > 0 && !this.active) {
      this.startDelayTimer();
    }
  }

  private addActivityListeners(): void {
    window.addEventListener('mousemove', this.boundResetDelayTimer);
    window.addEventListener('keydown', this.boundResetDelayTimer);
    window.addEventListener('click', this.boundResetDelayTimer);
    window.addEventListener('touchstart', this.boundResetDelayTimer);
    window.addEventListener('scroll', this.boundResetDelayTimer);
  }

  private removeActivityListeners(): void {
    window.removeEventListener('mousemove', this.boundResetDelayTimer);
    window.removeEventListener('keydown', this.boundResetDelayTimer);
    window.removeEventListener('click', this.boundResetDelayTimer);
    window.removeEventListener('touchstart', this.boundResetDelayTimer);
    window.removeEventListener('scroll', this.boundResetDelayTimer);
  }

  private showHint(): void {
    if (!this.active || this.hintVisible) return;

    this.hintVisible = true;
    if (this.hintEl) {
      this.hintEl.classList.add('visible');
    }

    // Auto-hide after 3 seconds
    if (this.hintTimeoutId) {
      clearTimeout(this.hintTimeoutId);
    }
    this.hintTimeoutId = setTimeout(() => {
      this.hideHint();
    }, 3000);
  }

  private hideHint(): void {
    this.hintVisible = false;
    if (this.hintEl) {
      this.hintEl.classList.remove('visible');
    }
    if (this.hintTimeoutId) {
      clearTimeout(this.hintTimeoutId);
      this.hintTimeoutId = null;
    }
  }

  private handleClick(event: MouseEvent | TouchEvent): void {
    // Get click/touch position
    let x: number, y: number;

    if (event instanceof TouchEvent && event.changedTouches.length > 0) {
      x = event.changedTouches[0].clientX;
      y = event.changedTouches[0].clientY;
    } else if (event instanceof MouseEvent) {
      x = event.clientX;
      y = event.clientY;
    } else {
      // Fallback to center
      x = window.innerWidth / 2;
      y = window.innerHeight / 2;
    }

    this.dispatchEvent(new CustomEvent('screensaver-click', { detail: { x, y } }));

    // Animate circle reveal from click position
    if (this.contentEl) {
      const duration = 700; // ms - slower for dramatic effect
      const startTime = performance.now();
      const maxSize = Math.max(window.innerWidth, window.innerHeight) * 1.5;
      const startSize = 20; // Start with small visible circle

      const animate = (currentTime: number) => {
        const elapsed = currentTime - startTime;
        const progress = Math.min(elapsed / duration, 1);

        // Ease-in curve: starts slow, picks up speed
        const eased = progress * progress * progress;
        const size = startSize + eased * (maxSize - startSize);

        this.contentEl!.style.maskImage = `radial-gradient(circle ${size}px at ${x}px ${y}px, transparent 100%, black 100%)`;
        this.contentEl!.style.webkitMaskImage = `radial-gradient(circle ${size}px at ${x}px ${y}px, transparent 100%, black 100%)`;

        if (progress < 1) {
          requestAnimationFrame(animate);
        } else {
          // Animation complete - remove screensaver
          this.active = false;
          EcoScreensaver.destroy();
        }
      };

      requestAnimationFrame(animate);
    } else {
      this.active = false;
    }
  }
}
