import { AfterViewInit, ChangeDetectionStrategy, Component, computed, ElementRef, input, output, signal, TemplateRef, ViewChild } from '@angular/core';
import { FAST_KENDO_COMMON } from '../../app.config';
import { FastButtonComponent } from './fast-button.component';
import { FastSVGComponent } from './fast-svg.component';
import { NgTemplateOutlet } from '@angular/common';
import { promptAction, PromptType } from '../services/message.service';
import * as util from '../utils/util';

@Component({
  selector: 'fast-prompt',
  imports: [FAST_KENDO_COMMON, FastButtonComponent, FastSVGComponent, NgTemplateOutlet],
  template: `
    <dialog #dialog
      [style.height]="useAutoHeight() ? 'fit-content' : currentHeight() + 'px'"
      [style.width]="useAutoWidth() ? 'fit-content' : currentWidth() + 'px'"
      [style.top.px]="currentTop()"
      [style.left.px]="currentLeft()"
      class="bg-base-white-500 dark:bg-alt-gray-500
        inset-auto overflow-hidden flex flex-col p-0 border-0 rounded-xl shadow-lg shadow-black z-2 max-w-none max-h-none">
      <header #promptTitlebar
        class="m-0 p-0 items-center justify-center border-none h-8.5 select-none" [class]="conditionalClasses()">
        <div class="max-w-full flex flex-row grow basis-full pl-1">
          <div class="overflow-hidden flex grow-0 items-start justify-center mt-0 mr-1 mb-0 ml-2">
            <span class="grow truncate text-lg font-bold">{{ title() }}</span>
          </div>
          <div class="flex grow gap-1"></div>
          <div class="titlebar-actions flex flex-row absolute right-0">
            <fast-button class="w-10 h-8.5 border-0 rounded-xl" type="transparent" (click)="closePrompt()" [tabIndex]="-1">
              <fast-svg icon="x" />
            </fast-button>
          </div>
        </div>
      </header>
      <div class="flex flex-col grow m-3 justify-start">
        <div #promptContentElem class="flex flex-col grow overflow-hidden text-wrap">
          @if (isTemplateRef(content())) {
            <ng-container *ngTemplateOutlet="asTemplateRef(content())" />
          } @else {
            {{ content() }}
          }
        </div>
        <div class="flex flex-row shrink-0">
          @if (promptType() === 'Custom' && customButtons()) {
            <ng-container *ngTemplateOutlet="customButtons(); context: { $implicit: promptAction, emit: emitResult.bind(this) }" />
          }
          @else if (promptType() === 'Yes-No') {
            <fast-button class="w-1/2 mt-2 mr-1" (click)="emitResult(promptAction.No)">
              <fast-svg icon="cancel" />No
            </fast-button>
            <fast-button type="primary" class="w-1/2 mt-2 ml-1" (click)="emitResult(promptAction.Yes)" #promptConfirmButton>
              <fast-svg icon="check" />Yes
            </fast-button>
          }
          @else if (promptType() === 'Save-DontSave-Cancel') {
            <fast-button type="primary" class="w-1/3 mt-2 ml-1" (click)="emitResult(promptAction.Save)" #promptConfirmButton>
              <fast-svg icon="check" />Save
            </fast-button>
            <fast-button class="w-1/3 mt-2 mx-1" (click)="emitResult(promptAction.DontSave)">
              <fast-svg icon="cancel" />Don't Save
            </fast-button>
            <fast-button class="w-1/3 mt-2 mr-1" (click)="emitResult(promptAction.Cancel)">
              <fast-svg icon="cancel" />Cancel
            </fast-button>
          }
          @else if (promptType() === 'Apply-Discard-Cancel') {
            <fast-button type="primary" class="w-1/3 mt-2 ml-1" (click)="emitResult(promptAction.Apply)" #promptConfirmButton>
              <fast-svg icon="check" />Apply Changes
            </fast-button>
            <fast-button class="w-1/3 mt-2 mx-1" (click)="emitResult(promptAction.Discard)">
              <fast-svg icon="cancel" />Discard Changes
            </fast-button>
            <fast-button class="w-1/3 mt-2 mr-1" (click)="emitResult(promptAction.Cancel)">
              <fast-svg icon="cancel" />Cancel
            </fast-button>
          }
        </div>
      </div>
    </dialog>
  `,
  styles: `
    :host {
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      z-index: auto;
    }

    dialog::backdrop {
      background: rgba(0, 0, 0, 0.4);
    }

    dialog:focus-visible {
      outline: none;
    }
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class FastPromptComponent implements AfterViewInit {
  @ViewChild('dialog') dialog!: ElementRef<HTMLDialogElement>;
  @ViewChild('promptTitlebar') promptTitlebar!: ElementRef<HTMLHeadElement>;
  @ViewChild('promptContentElem') promptContentElem: ElementRef<HTMLDivElement>;
  @ViewChild('promptConfirmButton') promptConfirmButton: FastButtonComponent;

  promptAction = promptAction;

  // Inputs
  title = input<string>('Please confirm');
  content = input<string | TemplateRef<unknown>>('Are you sure?');
  promptType = input<PromptType>('Yes-No');
  customButtons = input<TemplateRef<unknown> | null>(null);
  minHeight = input<number>(150);
  minWidth = input<number>(350);

  // Outputs
  promptResult = output<promptAction>();
  // eslint-disable-next-line @angular-eslint/no-output-native
  close = output();

  // Sizing signals
  currentHeight = signal<number>(150);
  currentWidth = signal<number>(350);
  currentTop = signal<number | null>(null);
  currentLeft = signal<number | null>(null);
  useAutoHeight = signal<boolean>(false);
  useAutoWidth = signal<boolean>(false);

  // Computed minimum width based on number of buttons
  effectiveMinWidth = computed(() => {
    const type = this.promptType();
    // 3-button layouts need more space
    if (type === 'Save-DontSave-Cancel' || type === 'Apply-Discard-Cancel') {
      return Math.max(this.minWidth(), 500);
    }
    return this.minWidth();
  });

  conditionalClasses = computed(() => {
    return {
      'bg-gradient-to-b': true,
      'from-base-white-1000': true,
      'via-base-white-1000': true,
      'to-base-white-250': true,
      'dark:bg-gradient-to-b': true,
      'dark:from-alt-gray-1000': true,
      'dark:via-alt-gray-1000': true,
      'dark:to-alt-gray-500': true,
    };
  });

  ngAfterViewInit(): void {
    const dialog = this.dialog.nativeElement;

    dialog.addEventListener('close', () => this.closePrompt());
    dialog.showModal();
    dialog.focus();

    // Click outside to close
    dialog.addEventListener('mousedown', (e: MouseEvent) => {
      if (e.target !== dialog) return;
      const rect = dialog.getBoundingClientRect();
      const isInDialog =
        rect.top <= e.clientY &&
        e.clientY <= rect.top + rect.height &&
        rect.left <= e.clientX &&
        e.clientX <= rect.left + rect.width;
      if (!isInDialog) {
        this.closePrompt();
      }
    });

    // Calculate dimensions
    const content = this.content();
    if (this.isTemplateRef(content)) {
      this.useAutoHeight.set(true);
      this.useAutoWidth.set(true);

      this.currentTop.set(Math.max(0, (window.innerHeight - this.minHeight()) / 2));
      this.currentLeft.set(Math.max(0, (window.innerWidth - this.effectiveMinWidth()) / 2));

      setTimeout(() => this.measureAndCenterPromptWindow(), 0);
    } else {
      const dimensions = this.calculateTextPromptDimensions();
      let initWidth = dimensions[0];
      let initHeight = dimensions[1];

      initWidth = Math.min(initWidth, window.innerWidth - 40);
      initHeight = Math.min(initHeight, window.innerHeight - 40);

      this.currentWidth.set(initWidth);
      this.currentHeight.set(initHeight);

      const top = Math.max(0, (window.innerHeight - initHeight) / 2);
      const left = Math.max(0, (window.innerWidth - initWidth) / 2);
      this.currentTop.set(top);
      this.currentLeft.set(left);
    }

    // Auto-focus confirm button
    setTimeout(() => {
      this.promptConfirmButton?.buttonElem?.nativeElement?.focus();
    }, 250);
  }

  emitResult(action: promptAction): void {
    this.promptResult.emit(action);
    this.close.emit();
  }

  protected closePrompt(): void {
    if (this.promptType() === 'Yes-No') {
      this.promptResult.emit(promptAction.No);
    } else {
      this.promptResult.emit(promptAction.Cancel);
    }
    this.close.emit();
  }

  protected isTemplateRef(value: unknown): value is TemplateRef<unknown> {
    return value instanceof TemplateRef;
  }

  protected asTemplateRef(value: string | TemplateRef<unknown>): TemplateRef<unknown> {
    return value as TemplateRef<unknown>;
  }

  private measureAndCenterPromptWindow(): void {
    const dialog = this.dialog.nativeElement;
    const rect = dialog.getBoundingClientRect();

    const maxWidth = window.innerWidth - 40;
    const maxHeight = window.innerHeight - 40;
    const finalWidth = Math.min(rect.width, maxWidth);
    const finalHeight = Math.min(rect.height, maxHeight);

    const top = Math.max(0, (window.innerHeight - finalHeight) / 2);
    const left = Math.max(0, (window.innerWidth - finalWidth) / 2);

    this.currentTop.set(top);
    this.currentLeft.set(left);

    if (rect.width > maxWidth || rect.height > maxHeight) {
      this.useAutoWidth.set(false);
      this.useAutoHeight.set(false);
      this.currentWidth.set(finalWidth);
      this.currentHeight.set(finalHeight);
    }
  }

  private calculateTextPromptDimensions(): [number, number] {
    const maxWidth = 800;
    const maxHeight = 600;
    const widthPadding = 80;
    const heightPadding = 100;
    let finalWindowWidth = this.effectiveMinWidth();
    let finalWindowHeight = this.minHeight();

    const content = this.content() as string;
    const computedStyle = window.getComputedStyle(this.promptContentElem.nativeElement);
    const contentByLine = content?.split('\n');
    let lineCount = 0;

    contentByLine.forEach((lineText) => {
      const lineTextWidth = util.getTextWidth(lineText ?? '', computedStyle.font);
      const totalWidthPadding = Math.ceil(lineTextWidth / (maxWidth - widthPadding)) * widthPadding;
      const lineTextWidthPlusPadding = lineTextWidth + totalWidthPadding;

      if (lineTextWidthPlusPadding > maxWidth)
        lineCount += Math.ceil(lineTextWidthPlusPadding / maxWidth);
      else
        lineCount += 1;

      const calculatedLineWidth = Math.min(maxWidth, lineTextWidthPlusPadding);
      finalWindowWidth = Math.max(finalWindowWidth, calculatedLineWidth);
    });

    lineCount = lineCount === 0 ? 1 : lineCount;
    const lineHeight = parseInt(computedStyle.lineHeight, 10);
    const totalLineTextHeight = (lineHeight * lineCount) + heightPadding;
    const calculatedWindowHeight = Math.min(maxHeight, totalLineTextHeight);
    finalWindowHeight = Math.max(finalWindowHeight, calculatedWindowHeight);

    return [finalWindowWidth, finalWindowHeight];
  }
}
