import {AfterViewInit, Component, ElementRef, OnDestroy, Renderer2} from '@angular/core';
import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatInputModule} from '@angular/material/input';
import {HeaderComponent} from '@header/header.component';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {FormsModule} from '@angular/forms';
import {MatDividerModule} from '@angular/material/divider';
import {MatButtonModule} from '@angular/material/button';

interface Part {
  text: string;
  gradient: boolean;
  colorA: string;
  colorB: string;
  continuation: boolean;
  invalid?: boolean;
}

@Component({
  selector: 'app-nick-generator',
  templateUrl: './nick-generator.component.html',
  styleUrls: ['./nick-generator.component.scss'],
  imports: [
    MatFormFieldModule,
    MatInputModule,
    HeaderComponent,
    MatCheckboxModule,
    FormsModule,
    MatDividerModule,
    MatButtonModule,
  ]
})
export class NickGeneratorComponent implements AfterViewInit, OnDestroy {
  parts: Part[] = [
    {text: '', gradient: false, colorA: '#ffffff', colorB: '#ffffff', continuation: false}
  ];

  tryCmd = '';
  requestCmd = '';
  previewHtml: SafeHtml = '';
  showPreview = false;
  showCommands = false;
  private handleResize: any;
  private boundHandleResize: any;
  private resizeObserver: ResizeObserver | null = null;


  constructor(private sanitizer: DomSanitizer,
              private elementRef: ElementRef,
              private renderer: Renderer2
  ) {
  }

  ngOnDestroy() {
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
      this.resizeObserver = null;
    }

    if (this.boundHandleResize) {
      window.removeEventListener('resize', this.boundHandleResize);
    }
  }

  ngAfterViewInit(): void {
    this.setupResizeObserver();
    window.addEventListener('resize', this.boundHandleResize);
    this.boundHandleResize = this.handleResize.bind(this);
    setTimeout(() => this.updateContainerHeight(), 0);
  }

  addPart(): void {
    this.parts.push({text: '', gradient: false, colorA: '#ffffff', colorB: '#ffffff', continuation: false});
    this.onInputChanged();
  }

  deletePart(): void {
    if (this.parts.length > 1) {
      this.parts.pop();
      // If last part was a gradient, unset continuation on new last part
      if (this.parts.length > 0) this.parts[this.parts.length - 1].continuation = false;
      this.onInputChanged();
    }
  }

  onGradientToggle(i: number): void {
    // Toggling gradient affects availability of continuation for this & next part
    if (!this.parts[i].gradient) {
      // If gradient turned off, force continuation off for this index (not visible anymore)
      this.parts[i].continuation = false;
    }
    if (i + 1 < this.parts.length && !this.parts[i + 1].gradient) {
      this.parts[i + 1].continuation = false;
    }
    this.onInputChanged();
  }

  onContinuationToggle(_: number): void {
    this.onInputChanged();
  }

  onInputChanged(): void {
    let result = '';
    let preview = '';
    let valid = true;
    let nickLen = 0;
    let prevColorB = '#ffffff';

    for (let i = 0; i < this.parts.length; i++) {
      const p = this.parts[i];
      const len = p.text.length;
      nickLen += len;

      const partValid =
        (p.gradient && len >= 1 && len <= 16) ||
        (!p.gradient && len > 0);

      p.invalid = !partValid;
      if (!partValid) {
        valid = false;
        continue;
      }

      if (p.gradient) {
        // Continuation allowed only if previous & current are gradient
        const contAllowed = i > 0 && this.parts[i - 1].gradient;
        const cont = p.continuation && contAllowed;

        if (cont) {
          result += p.text;
          preview += this.generateGradient(p.text, prevColorB, p.colorB);
        } else {
          result += `{${p.colorA}>}${p.text}`;
          preview += this.generateGradient(p.text, p.colorA, p.colorB);
        }

        // Add closing/continuation marker
        const nextContinuation = (i + 1 < this.parts.length) && this.parts[i + 1].continuation;
        if (i < this.parts.length - 1) {
          result += `{${p.colorB}<>}`;
        } else {
          result += `{${p.colorB}<}`;
        }
        prevColorB = p.colorB;
      } else {
        // Solid
        result += `{${p.colorA}}${p.text}`;
        preview += this.generateSolidColor(p.text, p.colorA);
      }
    }

    this.tryCmd = '';
    this.requestCmd = '';
    this.showPreview = false;
    this.showCommands = false;

    if (valid && result.length > 0 && nickLen >= 3 && nickLen <= 16) {
      this.tryCmd = `/nick try ${result}`;
      this.requestCmd = `/nick request ${result}`;
      this.previewHtml = this.sanitizer.bypassSecurityTrustHtml(
        this.generateSolidColor('Nickname preview: ', '#ffffff') + preview
      );
      this.showPreview = true;
      this.showCommands = true;
    } else {
      if (!valid && (this.parts.length > 1 || nickLen > 0)) {
        this.previewHtml = this.sanitizer.bypassSecurityTrustHtml(
          this.generateSolidColor('Invalid part(s) length', '#dd0000')
        );
      } else if (valid && (nickLen < 3 || nickLen > 16)) {
        this.previewHtml = this.sanitizer.bypassSecurityTrustHtml(
          this.generateSolidColor('Nickname needs to be 3–16 chars', '#dd0000')
        );
      } else {
        this.previewHtml = this.sanitizer.bypassSecurityTrustHtml('');
      }
      this.showPreview = nickLen > 0;
    }
  }

  tryCommandButtonContent = 'Copy';
  requestCommandButtonContent = 'Copy';

  copy(text: string, button: 'try' | 'request'): void {
    navigator.clipboard.writeText(text);
    if (button === 'try') {
      this.tryCommandButtonContent = 'Copied!';
    } else if (button === 'request') {
      this.requestCommandButtonContent = 'Copied!';
    }
    setTimeout(() => {
      if (button === 'try') {
        this.tryCommandButtonContent = 'Copy';
      } else if (button === 'request') {
        this.requestCommandButtonContent = 'Copy';
      }
    }, 1000);
  }

  generateSolidColor(text: string, color: string): string {
    return `<span style="color:${color}">${this.escape(text)}</span>`;
  }

  generateGradient(text: string, colorA: string, colorB: string): string {
    const len = text.length;
    if (len === 0) return '';
    const a = this.hexToRgb(colorA);
    const b = this.hexToRgb(colorB);
    if (!a || !b) return this.generateSolidColor(text, colorA);

    const stepR = len > 1 ? (b.r - a.r) / (len - 1) : 0;
    const stepG = len > 1 ? (b.g - a.g) / (len - 1) : 0;
    const stepB = len > 1 ? (b.b - a.b) / (len - 1) : 0;

    let res = '';
    for (let i = 0; i < len; i++) {
      const r = a.r + stepR * i;
      const g = a.g + stepG * i;
      const bl = a.b + stepB * i;
      res += this.generateSolidColor(text[i], this.rgbToHex(r, g, bl));
    }
    return res;
  }

  hexToRgb(hex: string): { r: number; g: number; b: number } | null {
    const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return m
      ? {r: parseInt(m[1], 16), g: parseInt(m[2], 16), b: parseInt(m[3], 16)}
      : null;
  }

  componentToHex(c: number): string {
    const x = Math.round(c);
    const h = x.toString(16);
    return h.length === 1 ? '0' + h : h;
  }

  rgbToHex(r: number, g: number, b: number): string {
    return (
      '#' + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b)
    );
  }

  escape(s: string): string {
    return s
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;');
  }

  private updateContainerHeight() {
    const headerElement = document.querySelector('app-header');
    const footerElement = document.querySelector('footer');

    const container = this.elementRef.nativeElement.querySelector('.containerNick');

    if (headerElement && footerElement && container) {
      const headerHeight = headerElement.getBoundingClientRect().height;
      const footerHeight = footerElement.getBoundingClientRect().height;

      const calculatedHeight = `calc(100vh - ${headerHeight}px - ${footerHeight}px)`;
      this.renderer.setStyle(container, 'min-height', calculatedHeight);
    }
  }

  private setupResizeObserver() {
    this.resizeObserver = new ResizeObserver(() => {
      this.updateContainerHeight();
    });

    const headerElement = document.querySelector('app-header');
    if (headerElement) {
      this.resizeObserver.observe(headerElement);
    }

    const footerElement = document.querySelector('footer');
    if (footerElement) {
      this.resizeObserver.observe(footerElement);
    }
  }
}
