import * as plugins from '../../../plugins.js';
import * as appstate from '../../../appstate.js';
import { viewHostCss } from '../../shared/index.js';
import {
  DeesElement,
  customElement,
  html,
  state,
  css,
  cssManager,
  type TemplateResult,
} from '@design.estate/dees-element';

type TViewMode = 'current' | 'project' | 'group' | 'error';
type TSortBy = 'created' | 'duration' | 'status';
type TTimeRange = '1h' | '6h' | '1d' | '3d' | '7d' | '30d';

@customElement('gitops-view-pipelines')
export class GitopsViewPipelines extends DeesElement {
  @state()
  accessor connectionsState: appstate.IConnectionsState = {
    connections: [],
    activeConnectionId: null,
  };

  @state()
  accessor dataState: appstate.IDataState = {
    projects: [],
    groups: [],
    secrets: [],
    pipelines: [],
    pipelineJobs: [],
    currentJobLog: '',
  };

  @state() accessor selectedConnectionId: string = '';
  @state() accessor selectedProjectId: string = '';
  @state() accessor selectedGroupId: string = '';
  @state() accessor viewMode: TViewMode = 'current';
  @state() accessor sortBy: TSortBy = 'created';
  @state() accessor timeRange: TTimeRange = '1d';
  @state() accessor isLoading: boolean = false;

  private _autoRefreshHandler: () => void;
  private _logPollInterval: ReturnType<typeof setInterval> | null = null;

  constructor() {
    super();
    const connSub = appstate.connectionsStatePart
      .select((s) => s)
      .subscribe((s) => { this.connectionsState = s; });
    this.rxSubscriptions.push(connSub);

    const dataSub = appstate.dataStatePart
      .select((s) => s)
      .subscribe((s) => { this.dataState = s; });
    this.rxSubscriptions.push(dataSub);

    this._autoRefreshHandler = () => this.handleAutoRefresh();
    document.addEventListener('gitops-auto-refresh', this._autoRefreshHandler);
  }

  public override disconnectedCallback() {
    super.disconnectedCallback();
    document.removeEventListener('gitops-auto-refresh', this._autoRefreshHandler);
    this.stopLogPolling();
  }

  private handleAutoRefresh(): void {
    this.loadPipelines();
  }

  public static styles = [
    cssManager.defaultStyles,
    viewHostCss,
    css`
      .status-badge {
        display: inline-block;
        padding: 2px 8px;
        border-radius: 4px;
        font-size: 12px;
        font-weight: 600;
        text-transform: uppercase;
      }
      .status-success { background: #1a3a1a; color: #00ff88; }
      .status-failed { background: #3a1a1a; color: #ff4444; }
      .status-running { background: #1a2a3a; color: #00acff; }
      .status-pending { background: #3a3a1a; color: #ffaa00; }
      .status-canceled { background: #2a2a2a; color: #999; }
    `,
  ];

  public render(): TemplateResult {
    const connectionOptions = this.connectionsState.connections.map((c) => ({
      option: `${c.name} (${c.providerType})`,
      key: c.id,
    }));

    const viewModeOptions = [
      { option: 'Current', key: 'current' },
      { option: 'Project', key: 'project' },
      { option: 'Group', key: 'group' },
      { option: 'Error', key: 'error' },
    ];

    const timeRangeOptions = [
      { option: '1 hour', key: '1h' },
      { option: '6 hours', key: '6h' },
      { option: '1 day', key: '1d' },
      { option: '3 days', key: '3d' },
      { option: '7 days', key: '7d' },
      { option: '30 days', key: '30d' },
    ];

    const sortByOptions = [
      { option: 'Created', key: 'created' },
      { option: 'Duration', key: 'duration' },
      { option: 'Status', key: 'status' },
    ];

    const projectOptions = this.dataState.projects.map((p) => ({
      option: p.fullPath || p.name,
      key: p.id,
    }));

    const groupOptions = this.dataState.groups.map((g) => ({
      option: g.fullPath || g.name,
      key: g.id,
    }));

    const showMultiProjectColumns = this.viewMode !== 'project';

    return html`
      <div class="view-title">Pipelines</div>
      <div class="view-description">View and manage CI/CD pipelines</div>
      <div class="toolbar">
        <dees-input-dropdown
          .label=${'Connection'}
          .options=${connectionOptions}
          .selectedOption=${connectionOptions.find((o) => o.key === this.selectedConnectionId) || connectionOptions[0]}
          @selectedOption=${(e: CustomEvent) => {
            this.selectedConnectionId = e.detail.key;
            this.onConnectionChange();
          }}
        ></dees-input-dropdown>
        <dees-input-dropdown
          .label=${'View'}
          .options=${viewModeOptions}
          .selectedOption=${viewModeOptions.find((o) => o.key === this.viewMode)}
          @selectedOption=${(e: CustomEvent) => {
            this.onViewModeChange(e.detail.key);
          }}
        ></dees-input-dropdown>
        ${this.viewMode === 'project' ? html`
          <dees-input-dropdown
            .label=${'Project'}
            .options=${projectOptions}
            .selectedOption=${projectOptions.find((o) => o.key === this.selectedProjectId) || projectOptions[0]}
            @selectedOption=${(e: CustomEvent) => {
              this.selectedProjectId = e.detail.key;
              this.loadPipelines();
            }}
          ></dees-input-dropdown>
        ` : ''}
        ${this.viewMode === 'group' ? html`
          <dees-input-dropdown
            .label=${'Group'}
            .options=${groupOptions}
            .selectedOption=${groupOptions.find((o) => o.key === this.selectedGroupId) || groupOptions[0]}
            @selectedOption=${(e: CustomEvent) => {
              this.selectedGroupId = e.detail.key;
              this.loadPipelines();
            }}
          ></dees-input-dropdown>
        ` : ''}
        <dees-input-dropdown
          .label=${'Time'}
          .options=${timeRangeOptions}
          .selectedOption=${timeRangeOptions.find((o) => o.key === this.timeRange)}
          @selectedOption=${(e: CustomEvent) => {
            this.timeRange = e.detail.key;
            this.loadPipelines();
          }}
        ></dees-input-dropdown>
        <dees-input-dropdown
          .label=${'Sort'}
          .options=${sortByOptions}
          .selectedOption=${sortByOptions.find((o) => o.key === this.sortBy)}
          @selectedOption=${(e: CustomEvent) => {
            this.sortBy = e.detail.key;
            this.loadPipelines();
          }}
        ></dees-input-dropdown>
        <dees-button @click=${() => this.loadPipelines()}>Refresh</dees-button>
      </div>
      <dees-table
        .heading1=${'CI/CD Pipelines'}
        .heading2=${this.isLoading ? 'Loading...' : `${this.dataState.pipelines.length} pipeline runs`}
        .data=${this.dataState.pipelines}
        .displayFunction=${(item: any) => {
          const row: any = {};
          row['ID'] = item.id;
          row['Status'] = item.status;
          if (showMultiProjectColumns) {
            row['Project'] = item.projectName;
          }
          row['Ref'] = item.ref;
          row['Duration'] = item.duration ? `${Math.round(item.duration)}s` : '-';
          row['Source'] = item.source;
          row['Created'] = item.createdAt ? new Date(item.createdAt).toLocaleString() : '-';
          return row;
        }}
        .dataActions=${[
          {
            name: 'View Logs',
            iconName: 'lucide:terminal',
            type: ['inRow', 'contextmenu'],
            actionFunc: async ({ item }: any) => { await this.openPipelineLogs(item); },
          },
          {
            name: 'View Jobs',
            iconName: 'lucide:list',
            type: ['contextmenu'],
            actionFunc: async ({ item }: any) => { await this.viewJobs(item); },
          },
          {
            name: 'Retry',
            iconName: 'lucide:refreshCw',
            type: ['inRow', 'contextmenu'],
            actionFunc: async ({ item }: any) => {
              await appstate.dataStatePart.dispatchAction(appstate.retryPipelineAction, {
                connectionId: item.connectionId || this.selectedConnectionId,
                projectId: item.projectId,
                pipelineId: item.id,
              });
              await this.loadPipelines();
            },
          },
          {
            name: 'Cancel',
            iconName: 'lucide:xCircle',
            type: ['contextmenu'],
            actionFunc: async ({ item }: any) => {
              await appstate.dataStatePart.dispatchAction(appstate.cancelPipelineAction, {
                connectionId: item.connectionId || this.selectedConnectionId,
                projectId: item.projectId,
                pipelineId: item.id,
              });
              await this.loadPipelines();
            },
          },
        ]}
      ></dees-table>
    `;
  }

  async firstUpdated() {
    await appstate.connectionsStatePart.dispatchAction(appstate.fetchConnectionsAction, null);

    // Check for navigation context from projects view
    const navCtx = appstate.uiStatePart.getState().navigationContext;
    if (navCtx?.connectionId && navCtx?.projectId) {
      this.selectedConnectionId = navCtx.connectionId;
      this.selectedProjectId = navCtx.projectId;
      this.viewMode = 'project';
      appstate.uiStatePart.dispatchAction(appstate.clearNavigationContextAction, null);
      await this.loadProjects();
      await this.loadPipelines();
      return;
    }

    const conns = appstate.connectionsStatePart.getState().connections;
    if (conns.length > 0 && !this.selectedConnectionId) {
      this.selectedConnectionId = conns[0].id;
      // In 'current' mode, load pipelines immediately
      await this.loadPipelines();
    }
  }

  // ---------------------------------------------------------------------------
  // Data loading
  // ---------------------------------------------------------------------------

  private async onConnectionChange() {
    this.selectedProjectId = '';
    this.selectedGroupId = '';
    if (this.viewMode === 'project') {
      await this.loadProjects();
    } else if (this.viewMode === 'group') {
      await this.loadGroups();
    } else {
      await this.loadPipelines();
    }
  }

  private onViewModeChange(newMode: TViewMode) {
    this.stopLogPolling();
    this.viewMode = newMode;
    this.selectedProjectId = '';
    this.selectedGroupId = '';

    if (newMode === 'current' || newMode === 'error') {
      this.loadPipelines();
    } else if (newMode === 'project') {
      this.loadProjects();
    } else if (newMode === 'group') {
      this.loadGroups();
    }
  }

  private async loadProjects() {
    if (!this.selectedConnectionId) return;
    await appstate.dataStatePart.dispatchAction(appstate.fetchProjectsAction, {
      connectionId: this.selectedConnectionId,
    });
  }

  private async loadGroups() {
    if (!this.selectedConnectionId) return;
    await appstate.dataStatePart.dispatchAction(appstate.fetchGroupsAction, {
      connectionId: this.selectedConnectionId,
    });
  }

  private async loadPipelines() {
    if (!this.selectedConnectionId) return;

    // For project mode, require a project selection
    if (this.viewMode === 'project' && !this.selectedProjectId) return;
    // For group mode, require a group selection
    if (this.viewMode === 'group' && !this.selectedGroupId) return;

    this.isLoading = true;
    try {
      await appstate.dataStatePart.dispatchAction(appstate.fetchPipelinesAction, {
        connectionId: this.selectedConnectionId,
        projectId: this.viewMode === 'project' ? this.selectedProjectId : undefined,
        viewMode: this.viewMode,
        groupId: this.viewMode === 'group' ? this.selectedGroupId : undefined,
        sortBy: this.sortBy,
        timeRange: this.timeRange,
      });
    } finally {
      this.isLoading = false;
    }
  }

  // ---------------------------------------------------------------------------
  // Pipeline log viewing
  // ---------------------------------------------------------------------------

  private async openPipelineLogs(pipeline: any) {
    await appstate.dataStatePart.dispatchAction(appstate.fetchPipelineJobsAction, {
      connectionId: pipeline.connectionId || this.selectedConnectionId,
      projectId: pipeline.projectId,
      pipelineId: pipeline.id,
    });

    const jobs = appstate.dataStatePart.getState().pipelineJobs;
    let activeJobId: string | null = null;

    const modal = await plugins.deesCatalog.DeesModal.createAndShow({
      heading: `Pipeline #${pipeline.id} - Logs`,
      content: html`
        <style>
          .log-container {
            display: flex;
            gap: 0;
            min-height: 400px;
            max-height: 70vh;
            color: #ccc;
            font-family: 'Intel One Mono', 'Fira Code', monospace;
          }
          .job-list {
            width: 220px;
            flex-shrink: 0;
            border-right: 1px solid #333;
            overflow-y: auto;
          }
          .job-entry {
            padding: 10px 14px;
            cursor: pointer;
            border-bottom: 1px solid #282828;
            transition: background 0.15s;
          }
          .job-entry:hover {
            background: #1a1a2e;
          }
          .job-entry.active {
            background: #1a2a3a;
            border-left: 3px solid #00acff;
            padding-left: 11px;
          }
          .job-name {
            font-weight: 600;
            font-size: 13px;
            color: #eee;
          }
          .job-meta {
            font-size: 11px;
            color: #888;
            margin-top: 3px;
          }
          .job-status-badge {
            display: inline-block;
            padding: 1px 6px;
            border-radius: 3px;
            font-size: 10px;
            font-weight: 600;
            text-transform: uppercase;
          }
          .job-status-badge.running { background: #1a2a3a; color: #00acff; }
          .job-status-badge.success { background: #1a3a1a; color: #00ff88; }
          .job-status-badge.failed { background: #3a1a1a; color: #ff4444; }
          .job-status-badge.pending { background: #3a3a1a; color: #ffaa00; }
          .job-status-badge.canceled { background: #2a2a2a; color: #999; }
          .log-output {
            flex: 1;
            overflow: auto;
            background: #0d0d0d;
            padding: 12px;
          }
          .log-output pre {
            margin: 0;
            white-space: pre-wrap;
            word-break: break-all;
            font-family: 'Intel One Mono', 'Fira Code', monospace;
            font-size: 12px;
            line-height: 1.6;
            color: #ccc;
          }
          .no-log {
            color: #666;
            padding: 40px 20px;
            text-align: center;
            font-size: 13px;
          }
        </style>
        <div class="log-container">
          <div class="job-list">
            ${jobs.map((job: any) => html`
              <div
                class="job-entry"
                data-job-id="${job.id}"
                @click=${async (e: Event) => {
                  // Update active state visually
                  const container = (e.target as HTMLElement).closest('.log-container');
                  container?.querySelectorAll('.job-entry').forEach((el: Element) => el.classList.remove('active'));
                  (e.target as HTMLElement).closest('.job-entry')?.classList.add('active');
                  activeJobId = job.id;
                  await this.selectJobForLog(job, pipeline, container);
                }}
              >
                <div class="job-name">${job.name}</div>
                <div class="job-meta">
                  <span class="job-status-badge ${job.status}">${job.status}</span>
                  ${job.stage ? ` ${job.stage}` : ''}
                  ${job.duration ? ` - ${Math.round(job.duration)}s` : ''}
                </div>
              </div>
            `)}
            ${jobs.length === 0 ? html`<div class="no-log">No jobs found.</div>` : ''}
          </div>
          <div class="log-output">
            <pre class="job-log-pre"><span class="no-log">Select a job to view its log.</span></pre>
          </div>
        </div>
      `,
      menuOptions: [
        {
          name: 'Close',
          action: async (modalRef: any) => {
            this.stopLogPolling();
            modalRef.destroy();
          },
        },
      ],
    });
  }

  private async selectJobForLog(job: any, pipeline: any, container: Element | null) {
    this.stopLogPolling();

    // Fetch initial log
    await this.fetchAndDisplayLog(job, pipeline, container);

    // If job is running/pending, poll every 3 seconds
    if (job.status === 'running' || job.status === 'pending' || job.status === 'waiting') {
      this._logPollInterval = setInterval(async () => {
        await this.fetchAndDisplayLog(job, pipeline, container);
      }, 3000);
    }
  }

  private async fetchAndDisplayLog(job: any, pipeline: any, container: Element | null) {
    try {
      await appstate.dataStatePart.dispatchAction(appstate.fetchJobLogAction, {
        connectionId: pipeline.connectionId || this.selectedConnectionId,
        projectId: pipeline.projectId,
        jobId: job.id,
      });

      const log = appstate.dataStatePart.getState().currentJobLog;
      const pre = container?.querySelector('.job-log-pre');
      if (pre) {
        pre.textContent = log || '(No output yet)';
        // Auto-scroll to bottom
        const logOutput = pre.closest('.log-output');
        if (logOutput) {
          logOutput.scrollTop = logOutput.scrollHeight;
        }
      }
    } catch (err) {
      console.error('Failed to fetch job log:', err);
    }
  }

  private stopLogPolling() {
    if (this._logPollInterval !== null) {
      clearInterval(this._logPollInterval);
      this._logPollInterval = null;
    }
  }

  // ---------------------------------------------------------------------------
  // Legacy job view (accessible via context menu)
  // ---------------------------------------------------------------------------

  private async viewJobs(pipeline: any) {
    await appstate.dataStatePart.dispatchAction(appstate.fetchPipelineJobsAction, {
      connectionId: pipeline.connectionId || this.selectedConnectionId,
      projectId: pipeline.projectId,
      pipelineId: pipeline.id,
    });

    const jobs = appstate.dataStatePart.getState().pipelineJobs;
    await plugins.deesCatalog.DeesModal.createAndShow({
      heading: `Pipeline #${pipeline.id} - Jobs`,
      content: html`
        <style>
          .jobs-list { color: #fff; }
          .job-item { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #333; }
          .job-name { font-weight: 600; }
          .job-status { text-transform: uppercase; font-size: 12px; }
        </style>
        <div class="jobs-list">
          ${jobs.map((job: any) => html`
            <div class="job-item">
              <span class="job-name">${job.name} (${job.stage})</span>
              <span class="job-status">${job.status} - ${job.duration ? `${Math.round(job.duration)}s` : '-'}</span>
            </div>
          `)}
          ${jobs.length === 0 ? html`<p>No jobs found.</p>` : ''}
        </div>
      `,
      menuOptions: [
        { name: 'Close', action: async (modal: any) => { modal.destroy(); } },
      ],
    });
  }
}
