import * as plugins from '../plugins.js';
import { apiService, type IS3Object } from '../services/index.js';
import { formatSize, getFileName } from '../utilities/index.js';
import { themeStyles } from '../styles/index.js';

const { html, css, cssManager, customElement, property, state, DeesElement } = plugins;
const { DeesContextmenu } = plugins.deesCatalog;

@customElement('tsview-s3-keys')
export class TsviewS3Keys extends DeesElement {
  @property({ type: String })
  public accessor bucketName: string = '';

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

  @property({ type: Number })
  public accessor refreshKey: number = 0;

  @state()
  private accessor allKeys: IS3Object[] = [];

  @state()
  private accessor prefixes: string[] = [];

  @state()
  private accessor loading: boolean = false;

  @state()
  private accessor selectedKey: string = '';

  @state()
  private accessor filterText: string = '';

  @state()
  private accessor showCreateDialog: boolean = false;

  @state()
  private accessor createDialogType: 'folder' | 'file' = 'folder';

  @state()
  private accessor createDialogPrefix: string = '';

  @state()
  private accessor createDialogName: string = '';

  public static styles = [
    cssManager.defaultStyles,
    themeStyles,
    css`
      :host {
        display: block;
        height: 100%;
        overflow: hidden;
      }

      .keys-container {
        display: flex;
        flex-direction: column;
        height: 100%;
      }

      .filter-bar {
        padding: 12px;
        border-bottom: 1px solid #333;
      }

      .filter-input {
        width: 100%;
        padding: 8px 12px;
        background: rgba(0, 0, 0, 0.3);
        border: 1px solid #444;
        border-radius: 6px;
        color: #fff;
        font-size: 14px;
      }

      .filter-input:focus {
        outline: none;
        border-color: #404040;
      }

      .filter-input::placeholder {
        color: #666;
      }

      .keys-list {
        flex: 1;
        overflow-y: auto;
      }

      table {
        width: 100%;
        border-collapse: collapse;
      }

      thead {
        position: sticky;
        top: 0;
        background: #1a1a1a;
        z-index: 1;
      }

      th {
        text-align: left;
        padding: 10px 12px;
        font-size: 12px;
        font-weight: 500;
        color: #666;
        text-transform: uppercase;
        border-bottom: 1px solid #333;
      }

      td {
        padding: 8px 12px;
        font-size: 13px;
        border-bottom: 1px solid #2a2a3e;
      }

      tr:hover td {
        background: rgba(255, 255, 255, 0.03);
      }

      tr.selected td {
        background: rgba(255, 255, 255, 0.08);
      }

      .key-cell {
        display: flex;
        align-items: center;
        gap: 8px;
        cursor: pointer;
      }

      .key-icon {
        width: 16px;
        height: 16px;
        flex-shrink: 0;
      }

      .folder-icon {
        color: #fbbf24;
      }

      .key-name {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }

      .size-cell {
        color: #888;
        font-variant-numeric: tabular-nums;
      }

      .empty-state {
        padding: 32px;
        text-align: center;
        color: #666;
      }

      .dialog-overlay {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: rgba(0, 0, 0, 0.7);
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 1000;
      }

      .dialog {
        background: #1e1e1e;
        border-radius: 12px;
        padding: 24px;
        min-width: 400px;
        box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
      }

      .dialog-title {
        font-size: 18px;
        font-weight: 600;
        margin-bottom: 16px;
        color: #fff;
      }

      .dialog-location {
        font-size: 12px;
        color: #888;
        margin-bottom: 12px;
        font-family: monospace;
      }

      .dialog-input {
        width: 100%;
        padding: 10px 12px;
        background: #141414;
        border: 1px solid #333;
        border-radius: 6px;
        color: #fff;
        font-size: 14px;
        margin-bottom: 8px;
        box-sizing: border-box;
      }

      .dialog-input:focus {
        outline: none;
        border-color: #e0e0e0;
      }

      .dialog-hint {
        font-size: 11px;
        color: #666;
        margin-bottom: 16px;
      }

      .dialog-actions {
        display: flex;
        gap: 12px;
        justify-content: flex-end;
      }

      .dialog-btn {
        padding: 8px 16px;
        border-radius: 6px;
        font-size: 14px;
        cursor: pointer;
        transition: all 0.2s;
      }

      .dialog-btn-cancel {
        background: transparent;
        border: 1px solid #444;
        color: #aaa;
      }

      .dialog-btn-cancel:hover {
        background: rgba(255, 255, 255, 0.05);
        color: #fff;
      }

      .dialog-btn-create {
        background: #404040;
        border: none;
        color: #fff;
      }

      .dialog-btn-create:hover {
        background: #505050;
      }

      .dialog-btn-create:disabled {
        opacity: 0.5;
        cursor: not-allowed;
      }
    `,
  ];

  async connectedCallback() {
    super.connectedCallback();
    await this.loadObjects();
  }

  updated(changedProperties: Map<string, unknown>) {
    if (changedProperties.has('bucketName') || changedProperties.has('currentPrefix') || changedProperties.has('refreshKey')) {
      this.loadObjects();
    }
  }

  private async loadObjects() {
    this.loading = true;
    try {
      const result = await apiService.listObjects(this.bucketName, this.currentPrefix, '/');
      this.allKeys = result.objects;
      this.prefixes = result.prefixes;
    } catch (err) {
      console.error('Error loading objects:', err);
      this.allKeys = [];
      this.prefixes = [];
    }
    this.loading = false;
  }

  private handleFilterInput(e: Event) {
    this.filterText = (e.target as HTMLInputElement).value;
  }

  private selectKey(key: string, isFolder: boolean) {
    this.selectedKey = key;

    if (isFolder) {
      this.dispatchEvent(
        new CustomEvent('navigate', {
          detail: { prefix: key },
          bubbles: true,
          composed: true,
        })
      );
    } else {
      this.dispatchEvent(
        new CustomEvent('key-selected', {
          detail: { key },
          bubbles: true,
          composed: true,
        })
      );
    }
  }

  private get filteredItems() {
    const filter = this.filterText.toLowerCase();
    const folders = this.prefixes
      .filter((p) => !filter || getFileName(p).toLowerCase().includes(filter))
      .map((p) => ({ key: p, isFolder: true, size: undefined }));
    const files = this.allKeys
      .filter((o) => !filter || getFileName(o.key).toLowerCase().includes(filter))
      .map((o) => ({ key: o.key, isFolder: false, size: o.size }));
    return [...folders, ...files];
  }

  private handleItemContextMenu(event: MouseEvent, key: string, isFolder: boolean) {
    event.preventDefault();

    if (isFolder) {
      DeesContextmenu.openContextMenuWithOptions(event, [
        {
          name: 'Open',
          iconName: 'lucide:folderOpen',
          action: async () => {
            this.selectKey(key, true);
          },
        },
        {
          name: 'Copy Path',
          iconName: 'lucide:copy',
          action: async () => {
            await navigator.clipboard.writeText(key);
          },
        },
        { divider: true },
        {
          name: 'New Folder Inside',
          iconName: 'lucide:folderPlus',
          action: async () => this.openCreateDialog('folder', key),
        },
        {
          name: 'New File Inside',
          iconName: 'lucide:filePlus',
          action: async () => this.openCreateDialog('file', key),
        },
        { divider: true },
        {
          name: 'Delete Folder',
          iconName: 'lucide:trash2',
          action: async () => {
            if (confirm(`Delete folder "${getFileName(key)}" and all its contents?`)) {
              const success = await apiService.deletePrefix(this.bucketName, key);
              if (success) {
                await this.loadObjects();
              }
            }
          },
        },
      ]);
    } else {
      DeesContextmenu.openContextMenuWithOptions(event, [
        {
          name: 'Preview',
          iconName: 'lucide:eye',
          action: async () => {
            this.selectKey(key, false);
          },
        },
        {
          name: 'Download',
          iconName: 'lucide:download',
          action: async () => {
            const url = await apiService.getObjectUrl(this.bucketName, key);
            const link = document.createElement('a');
            link.href = url;
            link.download = getFileName(key);
            link.click();
          },
        },
        {
          name: 'Copy Path',
          iconName: 'lucide:copy',
          action: async () => {
            await navigator.clipboard.writeText(key);
          },
        },
        { divider: true },
        {
          name: 'Delete',
          iconName: 'lucide:trash2',
          action: async () => {
            if (confirm(`Delete file "${getFileName(key)}"?`)) {
              const success = await apiService.deleteObject(this.bucketName, key);
              if (success) {
                await this.loadObjects();
              }
            }
          },
        },
      ]);
    }
  }

  private handleEmptySpaceContextMenu(event: MouseEvent) {
    // Only trigger if clicking on the container itself, not on items
    if ((event.target as HTMLElement).closest('tr')) return;
    event.preventDefault();

    DeesContextmenu.openContextMenuWithOptions(event, [
      {
        name: 'New Folder',
        iconName: 'lucide:folderPlus',
        action: async () => this.openCreateDialog('folder', this.currentPrefix),
      },
      {
        name: 'New File',
        iconName: 'lucide:filePlus',
        action: async () => this.openCreateDialog('file', this.currentPrefix),
      },
    ]);
  }

  private openCreateDialog(type: 'folder' | 'file', prefix: string) {
    this.createDialogType = type;
    this.createDialogPrefix = prefix;
    this.createDialogName = '';
    this.showCreateDialog = true;
  }

  private getContentType(ext: string): string {
    const contentTypes: Record<string, string> = {
      json: 'application/json',
      txt: 'text/plain',
      html: 'text/html',
      css: 'text/css',
      js: 'application/javascript',
      ts: 'text/typescript',
      md: 'text/markdown',
      xml: 'application/xml',
      yaml: 'text/yaml',
      yml: 'text/yaml',
      csv: 'text/csv',
    };
    return contentTypes[ext] || 'application/octet-stream';
  }

  private getDefaultContent(ext: string): string {
    const defaults: Record<string, string> = {
      json: '{\n  \n}',
      html: '<!DOCTYPE html>\n<html>\n<head>\n  <title></title>\n</head>\n<body>\n  \n</body>\n</html>',
      md: '# Title\n\n',
      txt: '',
    };
    return defaults[ext] || '';
  }

  private async handleCreate() {
    if (!this.createDialogName.trim()) return;

    const name = this.createDialogName.trim();
    let path: string;

    if (this.createDialogType === 'folder') {
      path = this.createDialogPrefix + name + '/.keep';
    } else {
      path = this.createDialogPrefix + name;
    }

    const ext = name.split('.').pop()?.toLowerCase() || '';
    const contentType = this.createDialogType === 'file' ? this.getContentType(ext) : 'application/octet-stream';
    const content = this.createDialogType === 'file' ? this.getDefaultContent(ext) : '';

    const success = await apiService.putObject(
      this.bucketName,
      path,
      btoa(content),
      contentType
    );

    if (success) {
      this.showCreateDialog = false;
      await this.loadObjects();
    }
  }

  private renderCreateDialog() {
    if (!this.showCreateDialog) return '';

    const isFolder = this.createDialogType === 'folder';
    const title = isFolder ? 'Create New Folder' : 'Create New File';
    const placeholder = isFolder ? 'folder-name or path/to/folder' : 'filename.txt or path/to/file.txt';

    return html`
      <div class="dialog-overlay" @click=${() => this.showCreateDialog = false}>
        <div class="dialog" @click=${(e: Event) => e.stopPropagation()}>
          <div class="dialog-title">${title}</div>
          <div class="dialog-location">
            Location: ${this.bucketName}/${this.createDialogPrefix}
          </div>
          <input
            type="text"
            class="dialog-input"
            placeholder=${placeholder}
            .value=${this.createDialogName}
            @input=${(e: InputEvent) => this.createDialogName = (e.target as HTMLInputElement).value}
            @keydown=${(e: KeyboardEvent) => e.key === 'Enter' && this.handleCreate()}
          />
          <div class="dialog-hint">
            Use "/" to create nested ${isFolder ? 'folders' : 'path'} (e.g., ${isFolder ? 'parent/child' : 'folder/file.txt'})
          </div>
          <div class="dialog-actions">
            <button class="dialog-btn dialog-btn-cancel" @click=${() => this.showCreateDialog = false}>
              Cancel
            </button>
            <button
              class="dialog-btn dialog-btn-create"
              ?disabled=${!this.createDialogName.trim()}
              @click=${() => this.handleCreate()}
            >
              Create
            </button>
          </div>
        </div>
      </div>
    `;
  }

  render() {
    return html`
      <div class="keys-container">
        <div class="filter-bar">
          <input
            type="text"
            class="filter-input"
            placeholder="Filter files..."
            .value=${this.filterText}
            @input=${this.handleFilterInput}
          />
        </div>

        <div class="keys-list" @contextmenu=${(e: MouseEvent) => this.handleEmptySpaceContextMenu(e)}>
          ${this.loading
            ? html`<div class="empty-state">Loading...</div>`
            : this.filteredItems.length === 0
              ? html`<div class="empty-state">No objects found</div>`
              : html`
                  <table>
                    <thead>
                      <tr>
                        <th>Name</th>
                        <th style="width: 100px;">Size</th>
                      </tr>
                    </thead>
                    <tbody>
                      ${this.filteredItems.map(
                        (item) => html`
                          <tr
                            class="${this.selectedKey === item.key ? 'selected' : ''}"
                            @click=${() => this.selectKey(item.key, item.isFolder)}
                            @contextmenu=${(e: MouseEvent) => this.handleItemContextMenu(e, item.key, item.isFolder)}
                          >
                            <td>
                              <div class="key-cell">
                                ${item.isFolder
                                  ? html`
                                      <svg class="key-icon folder-icon" viewBox="0 0 24 24" fill="currentColor">
                                        <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
                                      </svg>
                                    `
                                  : html`
                                      <svg class="key-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
                                      </svg>
                                    `}
                                <span class="key-name">${getFileName(item.key)}</span>
                              </div>
                            </td>
                            <td class="size-cell">
                              ${item.isFolder ? '-' : formatSize(item.size)}
                            </td>
                          </tr>
                        `
                      )}
                    </tbody>
                  </table>
                `}
        </div>
      </div>
      ${this.renderCreateDialog()}
    `;
  }
}
