import * as plugins from '../plugins.js';
import { changeStreamService, type IActivityEvent, type IMongoChangeEvent } from '../services/index.js';
import type { IStorageChangeEvent } from '@design.estate/dees-catalog';
import { themeStyles } from '../styles/index.js';

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

type TFilterMode = 'all' | 'mongodb' | 'storage';

@customElement('tsview-activity-stream')
export class TsviewActivityStream extends DeesElement {
  @state()
  private accessor events: IActivityEvent[] = [];

  @state()
  private accessor filterMode: TFilterMode = 'all';

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

  @state()
  private accessor isLoading: boolean = true;

  @state()
  private accessor autoScroll: boolean = true;

  @state()
  private accessor now: number = Date.now();

  private subscription: plugins.smartrx.rxjs.Subscription | null = null;
  private connectionSubscription: plugins.smartrx.rxjs.Subscription | null = null;
  private nowInterval: ReturnType<typeof setInterval> | null = null;

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

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

      .header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 16px;
        border-bottom: 1px solid #333;
      }

      .header-title {
        font-size: 18px;
        font-weight: 600;
        color: #fff;
        display: flex;
        align-items: center;
        gap: 12px;
      }

      .connection-status {
        display: flex;
        align-items: center;
        gap: 6px;
        font-size: 12px;
        font-weight: 400;
      }

      .status-dot {
        width: 8px;
        height: 8px;
        border-radius: 50%;
      }

      .status-dot.connected {
        background: #22c55e;
        box-shadow: 0 0 6px rgba(34, 197, 94, 0.5);
      }

      .status-dot.disconnected {
        background: #ef4444;
      }

      .status-dot.connecting {
        background: #f59e0b;
        animation: pulse 1s infinite;
      }

      @keyframes pulse {
        0%, 100% { opacity: 1; }
        50% { opacity: 0.5; }
      }

      .header-controls {
        display: flex;
        align-items: center;
        gap: 12px;
      }

      .filter-tabs {
        display: flex;
        gap: 4px;
      }

      .filter-tab {
        padding: 6px 12px;
        background: transparent;
        border: 1px solid #444;
        color: #888;
        border-radius: 4px;
        cursor: pointer;
        font-size: 12px;
        transition: all 0.2s;
      }

      .filter-tab:hover {
        border-color: #666;
        color: #aaa;
      }

      .filter-tab.active {
        background: rgba(255, 255, 255, 0.1);
        border-color: #666;
        color: #e0e0e0;
      }

      .auto-scroll-toggle {
        display: flex;
        align-items: center;
        gap: 6px;
        font-size: 12px;
        color: #888;
        cursor: pointer;
      }

      .auto-scroll-toggle input {
        cursor: pointer;
      }

      .events-list {
        flex: 1;
        overflow-y: auto;
        padding: 12px;
      }

      .event-item {
        display: flex;
        gap: 12px;
        padding: 12px;
        margin-bottom: 8px;
        background: rgba(0, 0, 0, 0.2);
        border-radius: 8px;
        cursor: pointer;
        transition: background 0.1s;
      }

      .event-item:hover {
        background: rgba(255, 255, 255, 0.05);
      }

      .event-time-col {
        width: 70px;
        flex-shrink: 0;
        text-align: right;
        font-size: 11px;
        color: #666;
        padding-top: 2px;
      }

      .event-icon {
        width: 36px;
        height: 36px;
        border-radius: 8px;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
      }

      .event-icon.mongodb {
        background: rgba(16, 185, 129, 0.2);
        color: #10b981;
      }

      .event-icon.s3 {
        background: rgba(245, 158, 11, 0.2);
        color: #f59e0b;
      }

      .event-icon svg {
        width: 18px;
        height: 18px;
      }

      .event-content {
        flex: 1;
        min-width: 0;
      }

      .event-header {
        display: flex;
        align-items: center;
        margin-bottom: 4px;
      }

      .event-title {
        font-size: 13px;
        font-weight: 500;
        color: #e0e0e0;
      }

      .event-details {
        font-size: 12px;
        color: #888;
        font-family: monospace;
      }

      .event-type {
        display: inline-block;
        padding: 2px 6px;
        border-radius: 4px;
        font-size: 10px;
        font-weight: 500;
        text-transform: uppercase;
        margin-right: 8px;
      }

      .event-type.insert, .event-type.add {
        background: rgba(34, 197, 94, 0.2);
        color: #4ade80;
      }

      .event-type.update, .event-type.modify {
        background: rgba(59, 130, 246, 0.2);
        color: #60a5fa;
      }

      .event-type.delete {
        background: rgba(239, 68, 68, 0.2);
        color: #f87171;
      }

      .event-type.replace {
        background: rgba(168, 85, 247, 0.2);
        color: #c084fc;
      }

      .event-type.drop, .event-type.invalidate {
        background: rgba(239, 68, 68, 0.3);
        color: #f87171;
      }

      .empty-state {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        height: 100%;
        color: #666;
        gap: 16px;
      }

      .empty-state svg {
        width: 64px;
        height: 64px;
        opacity: 0.5;
      }

      .empty-state p {
        font-size: 14px;
      }

      .loading-state {
        display: flex;
        align-items: center;
        justify-content: center;
        height: 100%;
        color: #888;
      }

      .event-path {
        color: #aaa;
        word-break: break-all;
      }
    `,
  ];

  async connectedCallback() {
    super.connectedCallback();
    this.nowInterval = setInterval(() => {
      this.now = Date.now();
    }, 1000);
    await this.initializeStreaming();
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    if (this.nowInterval) {
      clearInterval(this.nowInterval);
      this.nowInterval = null;
    }
    this.cleanup();
  }

  private async initializeStreaming() {
    this.isLoading = true;

    // Subscribe to connection status and trigger re-subscription on reconnect
    this.connectionSubscription = changeStreamService.connectionStatus$.subscribe(async (status) => {
      const wasConnected = this.isConnected;
      this.isConnected = status === 'connected';
      if (status === 'connected' && !wasConnected) {
        await this.setupSubscriptions();
        this.isLoading = false;
      }
    });

    try {
      await changeStreamService.connect();
      // setupSubscriptions() is triggered by connectionStatus$ subscriber above
    } catch (error) {
      console.error('Failed to initialize activity stream:', error);
      this.isConnected = false;
      this.isLoading = false;
    }
  }

  private async setupSubscriptions() {
    // Read buffered events (captured while on other tabs by app-level subscription)
    const buffered = changeStreamService.getBufferedActivity();
    if (buffered.length > 0) {
      this.events = buffered;
    } else {
      // Buffer empty (fresh start) — fetch from server
      const recentEvents = await changeStreamService.getRecentActivity(100);
      if (recentEvents.length > 0) {
        this.events = recentEvents;
      }
    }

    // Set up RxJS listener only once for new live events
    if (!this.subscription) {
      this.subscription = changeStreamService.getActivityStream().subscribe((event) => {
        this.events = [...this.events, event].slice(-500);
        if (this.autoScroll) {
          this.scrollToTop();
        }
      });
    }
  }

  private cleanup() {
    if (this.subscription) {
      this.subscription.unsubscribe();
      this.subscription = null;
    }
    if (this.connectionSubscription) {
      this.connectionSubscription.unsubscribe();
      this.connectionSubscription = null;
    }
    // DO NOT call changeStreamService.unsubscribeFromActivity()
    // The app-level subscription keeps the server sending events for buffering
  }

  private scrollToTop() {
    requestAnimationFrame(() => {
      const list = this.shadowRoot?.querySelector('.events-list');
      if (list) {
        list.scrollTop = 0;
      }
    });
  }

  private setFilterMode(mode: TFilterMode) {
    this.filterMode = mode;
  }

  private toggleAutoScroll() {
    this.autoScroll = !this.autoScroll;
  }

  private get filteredEvents(): IActivityEvent[] {
    let events = this.events;
    if (this.filterMode !== 'all') {
      events = events.filter((e) => e.source === this.filterMode);
    }
    return events.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
  }

  private formatTime(timestamp: string): string {
    const date = new Date(timestamp);
    return date.toLocaleTimeString('en-US', {
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
    });
  }

  private formatRelativeTime(timestamp: string): string {
    const date = new Date(timestamp);
    const diff = this.now - date.getTime();

    if (diff < 1000) {
      return 'now';
    } else if (diff < 60000) {
      const secs = Math.floor(diff / 1000);
      return `${secs}s ago`;
    } else if (diff < 3600000) {
      const mins = Math.floor(diff / 60000);
      return `${mins}m ago`;
    } else if (diff < 86400000) {
      const hours = Math.floor(diff / 3600000);
      return `${hours}h ago`;
    } else {
      return date.toLocaleDateString();
    }
  }

  private getEventTitle(event: IActivityEvent): string {
    if (event.source === 'mongodb') {
      const mongoEvent = event.event as IMongoChangeEvent;
      return `${mongoEvent.database}.${mongoEvent.collection}`;
    } else {
      const storageEvent = event.event as IStorageChangeEvent;
      return storageEvent.bucket;
    }
  }

  private getEventDetails(event: IActivityEvent): string {
    if (event.source === 'mongodb') {
      const mongoEvent = event.event as IMongoChangeEvent;
      if (mongoEvent.documentId) {
        return `Document: ${mongoEvent.documentId}`;
      }
      return '';
    } else {
      const storageEvent = event.event as IStorageChangeEvent;
      return storageEvent.key;
    }
  }

  private getEventType(event: IActivityEvent): string {
    return event.event.type;
  }

  private handleEventClick(event: IActivityEvent) {
    // Dispatch navigation event
    if (event.source === 'mongodb') {
      const mongoEvent = event.event as IMongoChangeEvent;
      this.dispatchEvent(
        new CustomEvent('navigate-to-mongo', {
          detail: {
            database: mongoEvent.database,
            collection: mongoEvent.collection,
            documentId: mongoEvent.documentId,
          },
          bubbles: true,
          composed: true,
        })
      );
    } else {
      const storageEvent = event.event as IStorageChangeEvent;
      this.dispatchEvent(
        new CustomEvent('navigate-to-s3', {
          detail: {
            bucket: storageEvent.bucket,
            key: storageEvent.key,
          },
          bubbles: true,
          composed: true,
        })
      );
    }
  }

  private renderMongoIcon() {
    return html`
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
        <ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
        <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path>
        <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
      </svg>
    `;
  }

  private renderS3Icon() {
    return html`
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
        <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"></path>
      </svg>
    `;
  }

  private getConnectionStatusText(): string {
    if (this.isConnected) {
      return 'Live';
    }
    return 'Disconnected';
  }

  render() {
    return html`
      <div class="activity-container">
        <div class="header">
          <div class="header-title">
            Activity Stream
            <div class="connection-status">
              <span class="status-dot ${this.isConnected ? 'connected' : 'disconnected'}"></span>
              ${this.getConnectionStatusText()}
            </div>
          </div>
          <div class="header-controls">
            <div class="filter-tabs">
              <button
                class="filter-tab ${this.filterMode === 'all' ? 'active' : ''}"
                @click=${() => this.setFilterMode('all')}
              >
                All
              </button>
              <button
                class="filter-tab ${this.filterMode === 'mongodb' ? 'active' : ''}"
                @click=${() => this.setFilterMode('mongodb')}
              >
                MongoDB
              </button>
              <button
                class="filter-tab ${this.filterMode === 'storage' ? 'active' : ''}"
                @click=${() => this.setFilterMode('storage')}
              >
                S3
              </button>
            </div>
            <label class="auto-scroll-toggle">
              <input
                type="checkbox"
                .checked=${this.autoScroll}
                @change=${this.toggleAutoScroll}
              />
              Auto-scroll
            </label>
          </div>
        </div>

        <div class="events-list">
          ${this.isLoading
            ? html`<div class="loading-state">Connecting to activity stream...</div>`
            : this.filteredEvents.length === 0
              ? html`
                  <div class="empty-state">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                      <path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
                    </svg>
                    <p>No activity yet. Changes will appear here in real-time.</p>
                  </div>
                `
              : this.filteredEvents.map(
                  (event) => html`
                    <div class="event-item" @click=${() => this.handleEventClick(event)}>
                      <div class="event-time-col" title=${this.formatTime(event.timestamp)}>
                        ${this.formatRelativeTime(event.timestamp)}
                      </div>
                      <div class="event-icon ${event.source}">
                        ${event.source === 'mongodb' ? this.renderMongoIcon() : this.renderS3Icon()}
                      </div>
                      <div class="event-content">
                        <div class="event-header">
                          <div class="event-title">
                            <span class="event-type ${this.getEventType(event)}">${this.getEventType(event)}</span>
                            ${this.getEventTitle(event)}
                          </div>
                        </div>
                        <div class="event-details">
                          <span class="event-path">${this.getEventDetails(event)}</span>
                        </div>
                      </div>
                    </div>
                  `
                )}
        </div>
      </div>
    `;
  }
}
