import { LitElement, html, css, unsafeCSS } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { when } from 'lit/directives/when.js';

import '@shoelace-style/shoelace/dist/components/card/card.js';
import '@shoelace-style/shoelace/dist/components/button/button.js';
import '@shoelace-style/shoelace/dist/components/button-group/button-group.js';
import '@shoelace-style/shoelace/dist/components/dropdown/dropdown.js';
import '@shoelace-style/shoelace/dist/components/menu/menu.js';
import '@shoelace-style/shoelace/dist/components/menu-item/menu-item.js';
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import '@shoelace-style/shoelace/dist/components/spinner/spinner.js';
import '@shoelace-style/shoelace/dist/components/select/select.js';
import '@shoelace-style/shoelace/dist/components/option/option.js';
import '@shoelace-style/shoelace/dist/components/badge/badge.js';
import '@shoelace-style/shoelace/dist/components/divider/divider.js';
import '@shoelace-style/shoelace/dist/components/tooltip/tooltip.js';
import '@shoelace-style/shoelace/dist/components/alert/alert.js';

import '../../components/project-filter-modal.ts';
import '../../components/single-issue-detail-view.ts';
import {
  listProjects,
  searchIssues,
  detectIssueDependencies,
  extendIssueDependencyScan,
  commitIssueDependencies, // Import the new function
} from '../../api';
import type {
  Project,
  Issue,
  DependencyPair, // Import the type
  IssueStatus,
} from '../../types';
import consoleStyles from '../../styles/console-styles.css?inline';
import '../../components/pagination-controls.ts';
import { getStatusVariant } from '../../utils/verdict';

@customElement('issues-dependencies-view')
export class IssuesDependenciesView extends LitElement {
  @state()
  private _allProjects: Project[] = [];

  @state()
  private _selectedProjectId: string | null = null;

  @state()
  private _issues: Issue[] = [];

  @state()
  private _dependencies: DependencyPair[] = [];

  @state()
  private _dependencyMap: Map<
    string,
    { blocks: DependencyPair[]; blockedBy: DependencyPair[] }
  > = new Map();

  @state()
  private _loadingIssues = false;

  @state()
  private _loadingDependencies = false;

  @state()
  private _error: string | null = null;

  @state()
  private _isFilterModalOpen = false;

  @state()
  private _selectedStatus: IssueStatus | null = 'opened';

  @state()
  private _currentPage = 1;

  @state()
  private _hasMorePages = false;

  @state()
  private _expandedRowKey: string | null = null;

  @state()
  private _expandingIssueId: string | null = null;

  @state()
  private _committingDependencies: Set<string> = new Set();

  @state()
  private _committingAllForIssue: string | null = null;

  private _pageSize = 10;

  async connectedCallback() {
    super.connectedCallback();
  }

  async firstUpdated() {
    await this._fetchProjects();
    this.parseUrlAndUpdateState();
    if (this._allProjects.length > 0 && !this._selectedProjectId) {
      this._selectedProjectId = this._allProjects[0].id;
    }
    this.fetchIssues();
    this._updateUrl();
  }

  private async _fetchProjects() {
    try {
      this._allProjects = await listProjects();
    } catch (error) {
      this._error = 'Failed to load projects.';
      console.error(error);
    }
  }

  async fetchIssues() {
    if (!this._selectedProjectId) return;

    this._loadingIssues = true;
    this._error = null;
    this._dependencies = []; // Clear old dependencies
    this._dependencyMap.clear();

    try {
      const skip = (this._currentPage - 1) * this._pageSize;
      const issues = await searchIssues({
        project_ids: [this._selectedProjectId],
        limit: this._pageSize,
        skip: skip,
        status: this._selectedStatus || undefined,
      });
      this._issues = issues;
      this._hasMorePages = issues.length === this._pageSize;

      if (issues.length > 0) {
        this.fetchDependencies(issues.map((i) => i.id));
      }
    } catch (error) {
      this._error = 'Failed to load issues.';
      console.error(error);
    } finally {
      this._loadingIssues = false;
    }
  }

  async fetchDependencies(issueIds: string[]) {
    this._loadingDependencies = true;
    try {
      const result = await detectIssueDependencies(issueIds);
      this._dependencies = result.dependencies;
      this._processDependencies();
    } catch (error) {
      console.error('Failed to detect dependencies', error);
      // Silently fail for now, or show a non-blocking error
    } finally {
      this._loadingDependencies = false;
    }
  }

  private async _expandScanForRow(issueId: string) {
    this._expandingIssueId = issueId;
    this._loadingDependencies = true;
    this._error = null;
    try {
      // Extend the scan starting from the selected issue
      const result = await extendIssueDependencyScan([issueId], 10); // Extend by 10 issues

      // Merge new dependencies with existing ones
      const newDependencyPairs = [...this._dependencies];
      const existingPairs = new Set(
        newDependencyPairs.map(
          (p) => `${p.source_issue_id}-${p.dependent_issue_id}`
        )
      );

      for (const dep of result.dependencies) {
        const pairKey = `${dep.source_issue_id}-${dep.dependent_issue_id}`;
        if (!existingPairs.has(pairKey)) {
          newDependencyPairs.push(dep);
          existingPairs.add(pairKey);
        }
      }

      this._dependencies = newDependencyPairs;
      // Refetch issues to get any newly referenced issues and re-process all dependencies
      await this.fetchIssues();
    } catch (error) {
      this._error = 'Failed to expand scan for issue.';
      console.error(error);
    } finally {
      this._loadingDependencies = false;
      this._expandingIssueId = null;
    }
  }

  private async _handleCommitDependency(dependency: DependencyPair) {
    const dependencyKey = `${dependency.source_issue_id}:${dependency.dependent_issue_id}`;
    this._committingDependencies.add(dependencyKey);
    this.requestUpdate();

    try {
      const response = await commitIssueDependencies([dependency]);
      if (response.dependencies.length > 0) {
        // Update the local state to reflect the change
        const committedDep = response.dependencies[0];
        this._dependencies = this._dependencies.map((dep) =>
          dep.source_issue_id === committedDep.source_issue_id &&
          dep.dependent_issue_id === committedDep.dependent_issue_id
            ? { ...dep, is_committed: true }
            : dep
        );
        this._processDependencies();
      }
    } catch (error) {
      console.error('Failed to commit dependency:', error);
      // Optionally, show an error message to the user
    } finally {
      this._committingDependencies.delete(dependencyKey);
      this.requestUpdate(); // Re-render the component
    }
  }

  private async _handleCommitAllDependencies(
    dependencies: DependencyPair[],
    issueId: string
  ) {
    if (dependencies.length === 0) return;

    this._committingAllForIssue = issueId;

    try {
      const response = await commitIssueDependencies(dependencies);
      if (response.dependencies.length > 0) {
        const committedKeys = new Set(
          response.dependencies.map(
            (d) => `${d.source_issue_id}:${d.dependent_issue_id}`
          )
        );

        this._dependencies = this._dependencies.map((dep) => {
          const key = `${dep.source_issue_id}:${dep.dependent_issue_id}`;
          if (committedKeys.has(key)) {
            return { ...dep, is_committed: true };
          }
          return dep;
        });

        this._processDependencies();
      }
    } catch (error) {
      console.error('Failed to commit all dependencies:', error);
    } finally {
      this._committingAllForIssue = null;
    }
  }

  private _handleProjectSelect(e: CustomEvent) {
    this._selectedProjectId = e.target.value;
    this._currentPage = 1;
    this._issues = [];
    this._dependencies = [];
    this._dependencyMap.clear();
    this.fetchIssues();
    this._updateUrl();
  }

  private _openFilterModal() {
    this._isFilterModalOpen = true;
  }

  private _applyFilters(e: CustomEvent) {
    const { status } = e.detail;
    this._selectedStatus = status;
    this._isFilterModalOpen = false;
    this._currentPage = 1; // Reset pagination
    this.fetchIssues();
    this._updateUrl();
  }

  private _goToNextPage() {
    if (this._hasMorePages) {
      this._currentPage++;
      this.fetchIssues();
      this._updateUrl();
    }
  }

  private _goToPreviousPage() {
    if (this._currentPage > 1) {
      this._currentPage--;
      this.fetchIssues();
      this._updateUrl();
    }
  }

  private _toggleRow(issueId: string) {
    if (this._expandedRowKey === issueId) {
      this._expandedRowKey = null;
    } else {
      this._expandedRowKey = issueId;
    }
    this._updateUrl();
  }

  private _processDependencies() {
    const newMap = new Map<
      string,
      { blocks: DependencyPair[]; blockedBy: DependencyPair[] }
    >();
    this._issues.forEach((issue) => {
      newMap.set(issue.id, { blocks: [], blockedBy: [] });
    });

    this._dependencies.forEach((dep) => {
      // Add to the 'blocks' list of the source issue
      if (newMap.has(dep.source_issue_id)) {
        newMap.get(dep.source_issue_id)!.blocks.push(dep);
      }
      // Add to the 'blockedBy' list of the dependent issue
      if (newMap.has(dep.dependent_issue_id)) {
        newMap.get(dep.dependent_issue_id)!.blockedBy.push(dep);
      }
    });

    this._dependencyMap = newMap;
  }

  private _renderDependencyDetails(
    deps: { blocks: DependencyPair[]; blockedBy: DependencyPair[] } | undefined,
    issueId: string
  ) {
    if (!deps || (deps.blocks.length === 0 && deps.blockedBy.length === 0)) {
      return html`
        <div class="detail-section">
          <h4>Dependencies</h4>
          <sl-alert variant="primary" open>
            <sl-icon slot="icon" name="info-circle"></sl-icon>
            No dependencies detected for this issue.
          </sl-alert>
        </div>
      `;
    }

    const allDependencies = [...deps.blocks, ...deps.blockedBy];
    const uncommittedDependencies = allDependencies.filter(
      (d) => !d.is_committed && !d.comes_from_tracker
    );

    const renderList = (
      items: DependencyPair[],
      type: 'blocks' | 'blockedBy'
    ) => {
      return html`
        <ul>
          ${items.map((d) => {
            const dependencyKey = `${d.source_issue_id}:${d.dependent_issue_id}`;
            const isCommitting =
              this._committingDependencies.has(dependencyKey);
            const issueId =
              type === 'blocks' ? d.dependent_issue_id : d.source_issue_id;
            const issue = this._issues.find((i) => i.id === issueId);

            return html`
              <li>
                <div class="dependency-info">
                  <strong
                    >${type === 'blocks'
                      ? d.dependency_key
                      : d.issue_key}</strong
                  >: ${issue?.title || 'Unknown Issue'}
                  <div class="dependency-reason">
                    ${d.reason} &bull; Confidence:
                    ${(d.confidence_score * 100).toFixed(0)}%
                  </div>
                </div>
                ${when(
                  !d.comes_from_tracker,
                  () =>
                    html`<sl-button
                      size="small"
                      variant="neutral"
                      @click="${() => this._handleCommitDependency(d)}"
                      ?disabled="${d.is_committed || isCommitting}"
                      .loading="${isCommitting}"
                    >
                      ${d.is_committed ? 'Committed' : 'Commit'}
                    </sl-button>`
                )}
              </li>
            `;
          })}
        </ul>
      `;
    };

    return html`
      <div class="detail-section dependency-details">
        <div class="dependencies-header">
          <h4>Dependencies</h4>
          <sl-button
            size="small"
            variant="primary"
            outline
            ?disabled=${uncommittedDependencies.length === 0 ||
            this._committingAllForIssue === issueId}
            .loading=${this._committingAllForIssue === issueId}
            @click="${() =>
              this._handleCommitAllDependencies(
                uncommittedDependencies,
                issueId
              )}"
          >
            Commit All
          </sl-button>
        </div>
        ${when(
          deps.blocks.length > 0,
          () => html`
            <h5>Blocks</h5>
            ${renderList(deps.blocks, 'blocks')}
          `
        )}
        ${when(
          deps.blockedBy.length > 0,
          () => html`
            <h5>Blocked By</h5>
            ${renderList(deps.blockedBy, 'blockedBy')}
          `
        )}
      </div>
    `;
  }

  private _renderIssueTable() {
    if (this._loadingIssues && this._issues.length === 0) {
      return html`<div class="loading-container"></div>`;
    }

    if (this._error) {
      return html`<div class="error">${this._error}</div>`;
    }

    if (this._issues.length === 0) {
      return html`
        <sl-alert variant="warning" open>
          <sl-icon slot="icon" name="info-circle"></sl-icon>
          <strong>No Issues Found</strong><br />
          No issues were found for the selected project, or the project is
          empty.
        </sl-alert>
      `;
    }

    return html`
      <sl-card class="table-card">
        <table class="styled-table">
          <thead>
            <tr>
              <th>ID</th>
              <th>Title</th>
              <th>Status</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            ${this._issues.map((issue) => {
              const deps = this._dependencyMap.get(issue.id);
              const isExpanded = this._expandedRowKey === issue.id;
              return html`
                <tr
                  class="clickable-row ${isExpanded ? 'row-expanded' : ''}"
                  @click=${() => this._toggleRow(issue.id)}
                >
                  <td>
                    <a
                      href="${issue.url}"
                      target="_blank"
                      @click=${(e: Event) => e.stopPropagation()}
                      >${issue.key}</a
                    >
                  </td>
                  <td>
                    ${issue.title}
                    <div class="dependency-tags">
                      ${when(
                        this._loadingDependencies,
                        () =>
                          html`<sl-spinner
                            style="font-size: 1em; vertical-align: middle;"
                          ></sl-spinner>`,
                        () => html`
                          ${when(
                            deps && deps.blocks.length > 0,
                            () => html`
                              <sl-badge
                                pill
                                class="blocks-badge ${deps.blocks.some(
                                  (d) => d.comes_from_tracker
                                )
                                  ? 'from-tracker'
                                  : ''}"
                              >
                                <div class="dependency-badge-content">
                                  <sl-tooltip content="Blocks">
                                    <sl-icon name="arrow-right-circle"></sl-icon
                                    >Blocks:
                                  </sl-tooltip>
                                  <span
                                    >${deps?.blocks.map(
                                      (d, i) => html`
                                        <sl-tooltip
                                          content="Reason: ${d.reason} | Confidence: ${(
                                            d.confidence_score * 100
                                          ).toFixed(0)}%"
                                        >
                                          <span
                                            class="${d.is_committed
                                              ? 'is-committed'
                                              : d.comes_from_tracker
                                                ? 'from-tracker'
                                                : ''}"
                                            >#${d.dependency_key.match(
                                              /\d+$/
                                            )?.[0]}</span
                                          > </sl-tooltip
                                        >${i < deps.blocks.length - 1
                                          ? ', '
                                          : ''}
                                      `
                                    )}</span
                                  >
                                </div>
                              </sl-badge>
                            `
                          )}
                          ${when(
                            deps && deps.blockedBy.length > 0,
                            () => html`
                              <sl-badge pill class="blocked-by-badge">
                                <div class="dependency-badge-content">
                                  <sl-tooltip content="Blocked by">
                                    <sl-icon name="arrow-left-circle"></sl-icon
                                    >Blocked by:
                                  </sl-tooltip>
                                  <span
                                    >${deps?.blockedBy.map(
                                      (d, i) => html`
                                        <sl-tooltip
                                          content="Reason: ${d.reason} | Confidence: ${(
                                            d.confidence_score * 100
                                          ).toFixed(0)}%"
                                        >
                                          <span
                                            class="${d.is_committed
                                              ? 'is-committed'
                                              : d.comes_from_tracker
                                                ? 'from-tracker'
                                                : ''}"
                                            >#${d.issue_key.match(
                                              /\d+$/
                                            )?.[0]}</span
                                          > </sl-tooltip
                                        >${i < deps.blockedBy.length - 1
                                          ? ', '
                                          : ''}
                                      `
                                    )}</span
                                  >
                                </div>
                              </sl-badge>
                            `
                          )}
                        `
                      )}
                    </div>
                  </td>
                  <td>
                    <sl-badge variant=${getStatusVariant(issue.status)}
                      >${issue.status}</sl-badge
                    >
                  </td>
                  <td>
                    <sl-button
                      size="small"
                      @click=${(e: Event) => {
                        e.stopPropagation();
                        this._expandScanForRow(issue.id);
                      }}
                      ?disabled=${this._loadingDependencies &&
                      this._expandingIssueId !== issue.id}
                      .loading=${this._expandingIssueId === issue.id}
                      variant=${this._expandingIssueId === issue.id
                        ? 'primary'
                        : 'default'}
                      >Expand Scan</sl-button
                    >
                  </td>
                </tr>
                ${isExpanded
                  ? html`
                      <tr class="inline-detail-row">
                        <td colspan="4">
                          <div class="detail-view-card">
                            <single-issue-detail-view .issue=${issue}>
                              <div slot="additional-info">
                                ${this._renderDependencyDetails(deps, issue.id)}
                              </div>
                            </single-issue-detail-view>
                          </div>
                        </td>
                      </tr>
                    `
                  : ''}
              `;
            })}
          </tbody>
        </table>
      </sl-card>
      <pagination-controls
        .currentPage=${this._currentPage}
        .hasMorePages=${this._hasMorePages}
        .loading=${this._loadingIssues}
        @prev-page=${this._goToPreviousPage}
        @next-page=${this._goToNextPage}
      ></pagination-controls>
    `;
  }

  private _updateUrl() {
    if (!window.location.pathname.includes('dependencies')) {
      return;
    }
    const params = new URLSearchParams();
    if (this._selectedProjectId) {
      params.set('project', this._selectedProjectId);
    }
    if (this._selectedStatus) {
      params.set('status', this._selectedStatus);
    }
    if (this._expandedRowKey) {
      params.set('selectedIssue', this._expandedRowKey);
    }
    params.set('page', this._currentPage.toString());
    const newUrl = `${window.location.pathname}?${params.toString()}`;
    window.history.pushState({}, '', newUrl);
  }

  private parseUrlAndUpdateState() {
    const params = new URLSearchParams(window.location.search);
    const projectId = params.get('project');
    if (projectId && this._allProjects.some((p) => p.id === projectId)) {
      this._selectedProjectId = projectId;
    }

    this._expandedRowKey = params.get('selectedIssue') || null;
    this._currentPage = Number(params.get('page')) || 1;
    const status = params.get('status') as IssueStatus | null;
    if (status && ['opened', 'closed', 'all'].includes(status)) {
      this._selectedStatus = status;
    } else {
      this._selectedStatus = 'opened';
    }
  }

  static styles = [
    unsafeCSS(consoleStyles),
    css`
      .container {
        display: flex;
        flex-direction: column;
        gap: var(--sl-spacing-medium);
      }
      .table-card {
        width: 100%;
        margin-bottom: var(--sl-spacing-medium);
      }
      .table-card {
        --padding: 0;
        border-spacing: 0;
      }
      .controls {
        display: flex;
        gap: var(--sl-spacing-medium);
        align-items: center;
      }
      sl-select {
        min-width: 250px;
      }
      .dependency-tags {
        display: flex;
        flex-wrap: wrap;
        gap: var(--sl-spacing-2x-small);
        padding-top: var(--sl-spacing-3x-small);
      }
      .dependency-tags sl-badge::part(base) {
        cursor: pointer;
      }
      .blocks-badge::part(base) {
        background-color: var(--sl-color-neutral-200);
        color: var(--sl-color-neutral-800);
        border: none;
      }
      .blocked-by-badge::part(base) {
        background-color: var(--sl-color-neutral-300);
        color: var(--sl-color-neutral-800);
        border: none;
      }
      .dependency-badge-content {
        display: flex;
        align-items: center;
        gap: var(--sl-spacing-3x-small);
      }
      .clickable-row {
        cursor: pointer;
      }
      .row-expanded {
        background-color: var(--sl-color-primary-50);
      }

      .placeholder-content {
        text-align: center;
      }

      .dependency-details ul {
        list-style-type: none;
        padding-left: var(--sl-spacing-medium);
      }
      .dependency-details li {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: var(--sl-spacing-x-small) 0;
      }
      .dependency-details li:last-child {
        border-bottom: none;
      }
      .dependency-reason {
        font-size: var(--sl-font-size-small);
        color: var(--sl-color-neutral-500);
      }
      .from-tracker {
        color: var(--sl-color-primary-600);
      }
      .is-committed {
        color: var(--sl-color-success-600);
        font-weight: var(--sl-font-weight-semibold);
      }

      .dependencies-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: var(--sl-spacing-small);
      }

      .dependencies-header h4 {
        margin: 0;
      }
    `,
  ];

  render() {
    return html`
      <project-filter-modal
        .isOpen=${this._isFilterModalOpen}
        .selectedStatus=${this._selectedStatus}
        .showProjects=${false}
        .showResolution=${false}
        @on-close=${() => (this._isFilterModalOpen = false)}
        @on-apply=${this._applyFilters}
      ></project-filter-modal>

      <view-header headerText="Issue Dependencies" width="wide">
        <div slot="main-column" class="controls">
          <sl-select
            placeholder="Select a project..."
            .value=${this._selectedProjectId}
            @sl-change=${this._handleProjectSelect}
            .disabled=${this._allProjects.length === 0}
          >
            ${this._allProjects.map(
              (proj) =>
                html`<sl-option value=${proj.id}>${proj.name}</sl-option>`
            )}
          </sl-select>
          <sl-button @click=${this._openFilterModal}>
            <sl-icon slot="prefix" name="filter"></sl-icon>
            Filter
          </sl-button>
        </div>
      </view-header>

      <div class="column-layout wide">
        <div class="main-column">
          <div class="container">
            ${when(
              this._selectedProjectId,
              () => this._renderIssueTable(),
              () => html`
                <div class="empty-state">
                  <sl-icon
                    name="diagram-3"
                    style="font-size: 3rem; margin-bottom: 1rem;"
                  ></sl-icon>
                  <h2>Select a Project</h2>
                  <p>
                    Please select a project from the dropdown above to view its
                    issues and detect dependencies.
                  </p>
                </div>
              `
            )}
          </div>
        </div>
        <div class="side-column">
          <sl-card class="detail-view-card">
            <div slot="header">Issue Details</div>
            ${when(
              this._expandedRowKey,
              () => {
                const issue = this._issues.find(
                  (i) => i.id === this._expandedRowKey
                );
                if (issue) {
                  return html`<single-issue-detail-view .issue=${issue}>
                    <div slot="additional-info">
                      ${this._renderDependencyDetails(
                        this._dependencyMap.get(issue.id),
                        issue.id
                      )}
                    </div>
                  </single-issue-detail-view>`;
                } else {
                  return html`<div class="placeholder-content">
                    <sl-icon name="info-circle"></sl-icon>
                    <p>Select an issue to see details.</p>
                  </div>`;
                }
              },
              () =>
                html`<div class="placeholder-content">
                  <sl-icon name="info-circle"></sl-icon>
                  <p>Select an issue to see details.</p>
                </div>`
            )}
          </sl-card>
        </div>
      </div>
    `;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'issues-dependencies-view': IssuesDependenciesView;
  }
}
