import {
  DeesElement,
  property,
  html,
  customElement,
  type TemplateResult,
  css,
  cssManager,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { MONACO_VERSION } from '../dees-workspace-monaco/version.js';
import { themeDefaultStyles } from '../../00theme.js';
import '../../00group-button/dees-button/dees-button.js';

import type * as monaco from 'monaco-editor';

declare global {
  interface HTMLElementTagNameMap {
    'dees-workspace-diff-editor': DeesWorkspaceDiffEditor;
  }
}

@customElement('dees-workspace-diff-editor')
export class DeesWorkspaceDiffEditor extends DeesElement {
  // DEMO
  public static demo = () => html`
    <dees-workspace-diff-editor
      .originalContent=${'function hello() {\n  console.log("Hello");\n}'}
      .modifiedContent=${'function hello() {\n  console.log("Hello World!");\n  return true;\n}'}
      .language=${'typescript'}
      .filePath=${'/demo/example.ts'}
    ></dees-workspace-diff-editor>
  `;
  public static demoGroup = 'Workspace';

  // INSTANCE
  public diffEditorDeferred = domtools.plugins.smartpromise.defer<monaco.editor.IStandaloneDiffEditor>();

  @property({ type: String })
  accessor originalContent: string = '';

  @property({ type: String })
  accessor modifiedContent: string = '';

  @property({ type: String })
  accessor originalLabel: string = 'Disk Version';

  @property({ type: String })
  accessor modifiedLabel: string = 'Local Version';

  @property({ type: String })
  accessor language: string = 'typescript';

  @property({ type: String })
  accessor filePath: string = '';

  private diffEditor: monaco.editor.IStandaloneDiffEditor | null = null;
  private monacoThemeSubscription: domtools.plugins.smartrx.rxjs.Subscription | null = null;
  private originalModel: monaco.editor.ITextModel | null = null;
  private modifiedModel: monaco.editor.ITextModel | null = null;

  constructor() {
    super();
    domtools.DomTools.setupDomTools();
  }

  public static styles = [
    themeDefaultStyles,
    cssManager.defaultStyles,
    css`
      :host {
        display: block;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
      }

      * {
        box-sizing: border-box;
      }

      .diff-wrapper {
        display: flex;
        flex-direction: column;
        height: 100%;
        width: 100%;
      }

      .diff-toolbar {
        height: 48px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 0 16px;
        background: ${cssManager.bdTheme('hsl(0 0% 96%)', 'hsl(0 0% 12%)')};
        border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')};
        flex-shrink: 0;
      }

      .diff-info {
        display: flex;
        align-items: center;
        gap: 12px;
        font-size: 14px;
        color: ${cssManager.bdTheme('hsl(0 0% 30%)', 'hsl(0 0% 80%)')};
      }

      .diff-filename {
        font-weight: 600;
        color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')};
      }

      .diff-labels {
        font-size: 12px;
        color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 60%)')};
      }

      .diff-actions {
        display: flex;
        align-items: center;
        gap: 8px;
      }

      .diff-container {
        flex: 1;
        min-height: 0;
        width: 100%;
      }

      .nav-buttons {
        display: flex;
        gap: 4px;
      }

      .action-buttons {
        display: flex;
        gap: 8px;
        margin-left: 16px;
      }
    `,
  ];

  public render(): TemplateResult {
    const fileName = this.filePath.split('/').pop() || 'file';

    return html`
      <div class="diff-wrapper">
        <div class="diff-toolbar">
          <div class="diff-info">
            <span class="diff-filename">${fileName}</span>
            <span class="diff-labels">${this.originalLabel} ↔ ${this.modifiedLabel}</span>
          </div>
          <div class="diff-actions">
            <div class="nav-buttons">
              <dees-button
                type="outline"
                @click=${this.goToPreviousDiff}
              >Previous</dees-button>
              <dees-button
                type="outline"
                @click=${this.goToNextDiff}
              >Next</dees-button>
            </div>
            <div class="action-buttons">
              <dees-button
                type="highlighted"
                @click=${this.acceptLocal}
              >Use Local</dees-button>
              <dees-button
                type="outline"
                @click=${this.acceptDisk}
              >Use Disk</dees-button>
              <dees-button
                type="outline"
                @click=${this.close}
              >Close</dees-button>
            </div>
          </div>
        </div>
        <div class="diff-container"></div>
      </div>
    `;
  }

  public async firstUpdated(): Promise<void> {
    await super.firstUpdated(new Map());
    await this.initDiffEditor();
  }

  private async initDiffEditor(): Promise<void> {
    const container = this.shadowRoot?.querySelector('.diff-container') as HTMLElement;
    if (!container) return;

    const monacoCdnBase = `https://cdn.jsdelivr.net/npm/monaco-editor@${MONACO_VERSION}`;

    // Wait for Monaco to be loaded (should already be loaded by dees-workspace-monaco)
    let monacoInstance = (window as any).monaco as typeof monaco;

    if (!monacoInstance) {
      // Monaco not loaded yet, wait for it
      await new Promise<void>((resolve) => {
        const checkMonaco = setInterval(() => {
          if ((window as any).monaco) {
            clearInterval(checkMonaco);
            resolve();
          }
        }, 100);
      });
      monacoInstance = (window as any).monaco as typeof monaco;
    }

    // Get current theme from domtools
    const domtoolsInstance = await this.domtoolsPromise;
    const isBright = domtoolsInstance.themeManager.goBrightBoolean;
    const initialTheme = isBright ? 'vs' : 'vs-dark';

    // Create unique URIs for models
    const timestamp = Date.now();
    const originalUri = monacoInstance.Uri.parse(`diff://original/${timestamp}${this.filePath}`);
    const modifiedUri = monacoInstance.Uri.parse(`diff://modified/${timestamp}${this.filePath}`);

    // Create models
    this.originalModel = monacoInstance.editor.createModel(
      this.originalContent,
      this.language,
      originalUri
    );
    this.modifiedModel = monacoInstance.editor.createModel(
      this.modifiedContent,
      this.language,
      modifiedUri
    );

    // Create diff editor
    this.diffEditor = monacoInstance.editor.createDiffEditor(container, {
      automaticLayout: true,
      readOnly: false, // Allow editing the modified (local) side
      originalEditable: false, // Disk version is read-only
      renderSideBySide: true,
      ignoreTrimWhitespace: false,
      fontSize: 14,
      minimap: {
        enabled: false,
      },
    });

    // Set the theme
    monacoInstance.editor.setTheme(initialTheme);

    this.diffEditor.setModel({
      original: this.originalModel,
      modified: this.modifiedModel,
    });

    // Subscribe to theme changes
    this.monacoThemeSubscription = domtoolsInstance.themeManager.themeObservable.subscribe(
      (goBright: boolean) => {
        const newTheme = goBright ? 'vs' : 'vs-dark';
        monacoInstance.editor.setTheme(newTheme);
      }
    );

    // Inject Monaco CSS if not already present
    const cssId = 'monaco-diff-editor-css';
    if (!this.shadowRoot?.getElementById(cssId)) {
      const cssResponse = await fetch(`${monacoCdnBase}/min/vs/editor/editor.main.css`);
      const cssText = await cssResponse.text();
      const styleElement = document.createElement('style');
      styleElement.id = cssId;
      styleElement.textContent = cssText;
      this.shadowRoot?.append(styleElement);
    }

    // Navigate to first diff after a short delay
    setTimeout(() => {
      try {
        this.diffEditor?.revealFirstDiff();
      } catch {
        // Ignore if no diffs
      }
    }, 100);

    this.diffEditorDeferred.resolve(this.diffEditor);
  }

  public goToNextDiff(): void {
    try {
      this.diffEditor?.goToDiff('next');
    } catch {
      // Ignore if no more diffs
    }
  }

  public goToPreviousDiff(): void {
    try {
      this.diffEditor?.goToDiff('previous');
    } catch {
      // Ignore if no more diffs
    }
  }

  public acceptLocal(): void {
    // User wants to keep local version (potentially with edits made in diff view)
    const modifiedContent = this.diffEditor?.getModifiedEditor().getValue() || this.modifiedContent;
    this.dispatchEvent(
      new CustomEvent('diff-resolved', {
        detail: { action: 'use-local', content: modifiedContent },
        bubbles: true,
        composed: true,
      })
    );
  }

  public acceptDisk(): void {
    // User wants disk version
    this.dispatchEvent(
      new CustomEvent('diff-resolved', {
        detail: { action: 'use-disk', content: this.originalContent },
        bubbles: true,
        composed: true,
      })
    );
  }

  public close(): void {
    this.dispatchEvent(
      new CustomEvent('diff-closed', {
        bubbles: true,
        composed: true,
      })
    );
  }

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

    if (this.monacoThemeSubscription) {
      this.monacoThemeSubscription.unsubscribe();
      this.monacoThemeSubscription = null;
    }

    // Dispose models
    if (this.originalModel) {
      this.originalModel.dispose();
      this.originalModel = null;
    }
    if (this.modifiedModel) {
      this.modifiedModel.dispose();
      this.modifiedModel = null;
    }

    // Dispose editor
    if (this.diffEditor) {
      this.diffEditor.dispose();
      this.diffEditor = null;
    }
  }
}
