import * as appstate from '../../appstate.js';
import * as interfaces from '../../../dist_ts_interfaces/index.js';
import { viewHostCss } from '../shared/css.js';

import {
  DeesElement,
  css,
  cssManager,
  customElement,
  html,
  state,
  type TemplateResult,
} from '@design.estate/dees-element';

type TApiTokenScope = interfaces.data.TApiTokenScope;

@customElement('ops-view-apitokens')
export class OpsViewApiTokens extends DeesElement {
  @state() accessor routeState: appstate.IRouteManagementState = {
    mergedRoutes: [],
    warnings: [],
    httpRedirects: [],
    apiTokens: [],
    gatewayClients: [],
    isLoading: false,
    error: null,
    lastUpdated: 0,
  };

  constructor() {
    super();
    const sub = appstate.routeManagementStatePart
      .select((s) => s)
      .subscribe((routeState) => {
        this.routeState = routeState;
      });
    this.rxSubscriptions.push(sub);

    // Re-fetch tokens when user logs in (fixes race condition where
    // the view is created before authentication completes)
    const loginSub = appstate.loginStatePart
      .select((s) => s.isLoggedIn)
      .subscribe((isLoggedIn) => {
        if (isLoggedIn) {
          appstate.routeManagementStatePart.dispatchAction(appstate.fetchApiTokensAction, null);
        }
      });
    this.rxSubscriptions.push(loginSub);
  }

  public static styles = [
    cssManager.defaultStyles,
    viewHostCss,
    css`
      .apiTokensContainer {
        display: flex;
        flex-direction: column;
        gap: 24px;
      }

      .scopePill {
        display: inline-flex;
        align-items: center;
        padding: 2px 6px;
        border-radius: 3px;
        font-size: 11px;
        background: ${cssManager.bdTheme('rgba(0, 130, 200, 0.1)', 'rgba(0, 170, 255, 0.1)')};
        color: ${cssManager.bdTheme('#0369a1', '#0af')};
        margin-right: 4px;
        margin-bottom: 2px;
      }

      .statusBadge {
        display: inline-flex;
        align-items: center;
        padding: 3px 10px;
        border-radius: 12px;
        font-size: 12px;
        font-weight: 600;
        letter-spacing: 0.02em;
        text-transform: uppercase;
      }

      .statusBadge.active {
        background: ${cssManager.bdTheme('#dcfce7', '#14532d')};
        color: ${cssManager.bdTheme('#166534', '#4ade80')};
      }

      .statusBadge.disabled {
        background: ${cssManager.bdTheme('#fef2f2', '#450a0a')};
        color: ${cssManager.bdTheme('#991b1b', '#f87171')};
      }

      .statusBadge.expired {
        background: ${cssManager.bdTheme('#f3f4f6', '#374151')};
        color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
      }
    `,
  ];

  public render(): TemplateResult {
    const { apiTokens } = this.routeState;

    return html`
      <dees-heading level="3">API Tokens</dees-heading>

      <div class="apiTokensContainer">
        <dees-table
          .heading1=${'API Tokens'}
          .heading2=${'Manage programmatic access tokens'}
          .data=${apiTokens}
          .dataName=${'token'}
          .searchable=${true}
          .showColumnFilters=${true}
          .displayFunction=${(token: interfaces.data.IApiTokenInfo) => ({
            name: token.name,
            scopes: this.renderScopePills(token.scopes),
            status: this.renderStatusBadge(token),
            created: new Date(token.createdAt).toLocaleDateString(),
            expires: token.expiresAt ? new Date(token.expiresAt).toLocaleDateString() : 'Never',
            lastUsed: token.lastUsedAt ? new Date(token.lastUsedAt).toLocaleDateString() : 'Never',
          })}
          .dataActions=${[
            {
              name: 'Create Token',
              iconName: 'lucide:plus',
              type: ['header'],
              actionFunc: async () => {
                await this.showCreateTokenDialog();
              },
            },
            {
              name: 'Enable',
              iconName: 'lucide:play',
              type: ['inRow', 'contextmenu'] as any,
              actionRelevancyCheckFunc: (actionData: any) => !actionData.item.enabled,
              actionFunc: async (actionData: any) => {
                const token = actionData.item as interfaces.data.IApiTokenInfo;
                await appstate.routeManagementStatePart.dispatchAction(
                  appstate.toggleApiTokenAction,
                  { id: token.id, enabled: true },
                );
              },
            },
            {
              name: 'Disable',
              iconName: 'lucide:pause',
              type: ['inRow', 'contextmenu'] as any,
              actionRelevancyCheckFunc: (actionData: any) => actionData.item.enabled,
              actionFunc: async (actionData: any) => {
                const token = actionData.item as interfaces.data.IApiTokenInfo;
                await appstate.routeManagementStatePart.dispatchAction(
                  appstate.toggleApiTokenAction,
                  { id: token.id, enabled: false },
                );
              },
            },
            {
              name: 'Roll',
              iconName: 'lucide:rotateCw',
              type: ['inRow', 'contextmenu'] as any,
              actionFunc: async (actionData: any) => {
                const token = actionData.item as interfaces.data.IApiTokenInfo;
                await this.showRollTokenDialog(token);
              },
            },
            {
              name: 'Revoke',
              iconName: 'lucide:trash2',
              type: ['inRow', 'contextmenu'] as any,
              actionFunc: async (actionData: any) => {
                const token = actionData.item as interfaces.data.IApiTokenInfo;
                await appstate.routeManagementStatePart.dispatchAction(
                  appstate.revokeApiTokenAction,
                  token.id,
                );
              },
            },
          ]}
        ></dees-table>
      </div>
    `;
  }

  private renderScopePills(scopes: TApiTokenScope[]): TemplateResult {
    return html`<div style="display: flex; flex-wrap: wrap; gap: 2px;">${scopes.map(
      (s) => html`<span class="scopePill">${s}</span>`,
    )}</div>`;
  }

  private renderStatusBadge(token: interfaces.data.IApiTokenInfo): TemplateResult {
    if (!token.enabled) {
      return html`<span class="statusBadge disabled">Disabled</span>`;
    }
    if (token.expiresAt && token.expiresAt < Date.now()) {
      return html`<span class="statusBadge expired">Expired</span>`;
    }
    return html`<span class="statusBadge active">Active</span>`;
  }

  private async showCreateTokenDialog() {
    const { DeesModal } = await import('@design.estate/dees-catalog');

    const allScopes = [...interfaces.data.apiTokenScopes];

    await DeesModal.createAndShow({
      heading: 'Create API Token',
      content: html`
        <div style="color: #888; margin-bottom: 12px; font-size: 13px;">
          The token value will be shown once after creation. Copy it immediately.
        </div>
        <dees-form>
          <dees-input-text .key=${'name'} .label=${'Token Name'} .required=${true}></dees-input-text>
          <dees-input-tags
            .key=${'scopes'}
            .label=${'Token Scopes'}
            .value=${['gateway-clients:read', 'gateway-clients:write']}
            .suggestions=${allScopes}
            .required=${true}
          ></dees-input-tags>
          <dees-input-text .key=${'policyRole'} .label=${'Policy Role'} .description=${'admin, gatewayClient, or operator'}></dees-input-text>
          <dees-input-text .key=${'gatewayClientType'} .label=${'Gateway Client Type'} .description=${'For gatewayClient tokens: onebox, cloudly, or custom'}></dees-input-text>
          <dees-input-text .key=${'gatewayClientId'} .label=${'Gateway Client ID'} .description=${'Required for gatewayClient tokens'}></dees-input-text>
          <dees-input-text .key=${'hostnamePatterns'} .label=${'Hostname Patterns'} .description=${'Comma separated, e.g. *.apps.example.com'}></dees-input-text>
          <dees-input-text .key=${'allowedRouteTarget'} .label=${'Allowed Route Target'} .description=${'Optional host:ports, e.g. 203.0.113.10:80,443'}></dees-input-text>
          <dees-input-text .key=${'expiresInDays'} .label=${'Expires in'} .description=${'Number of days; leave blank for no expiration'}></dees-input-text>
        </dees-form>
      `,
      menuOptions: [
        {
          name: 'Cancel',
          iconName: 'lucide:x',
          action: async (modalArg: any) => await modalArg.destroy(),
        },
        {
          name: 'Create',
          iconName: 'lucide:key',
          action: async (modalArg: any) => {
            const contentEl = modalArg.shadowRoot?.querySelector('.content');
            const form = contentEl?.querySelector('dees-form');
            if (!form) return;
            const formData = await form.collectFormData();
            if (!formData.name) return;

            // dees-input-tags is not in dees-form's FORM_INPUT_TYPES, so collectFormData() won't
            // include it. Query the tags input directly and call getValue().
            const tagsInput = form.querySelector('dees-input-tags') as any;
            const rawScopes: string[] = tagsInput?.getValue?.() || tagsInput?.value || formData.scopes || [];
            const scopes = rawScopes
              .filter((s: string) => allScopes.includes(s as any)) as TApiTokenScope[];
            const policy = this.buildPolicy(formData, scopes);

            const expiresInDays = formData.expiresInDays
              ? parseInt(formData.expiresInDays, 10)
              : null;

            await modalArg.destroy();

            try {
              const response = await appstate.createApiToken(formData.name, scopes, expiresInDays, policy);
              if (response.success && response.tokenValue) {
                // Refresh the list first so it's ready when user dismisses the modal
                await appstate.routeManagementStatePart.dispatchAction(appstate.fetchApiTokensAction, null);

                // Show the token value in a new modal
                await DeesModal.createAndShow({
                  heading: 'Token Created',
                  content: html`
                    <div style="color: #ccc; padding: 8px 0;">
                      <p>Copy this token now. It will not be shown again.</p>
                      <div style="background: #111; padding: 12px; border-radius: 6px; margin-top: 8px;">
                        <code style="color: #0f8; word-break: break-all; font-size: 13px;">${response.tokenValue}</code>
                      </div>
                    </div>
                  `,
                  menuOptions: [
                    {
                      name: 'Done',
                      iconName: 'lucide:check',
                      action: async (m: any) => await m.destroy(),
                    },
                  ],
                });
              }
            } catch (error) {
              console.error('Failed to create token:', error);
            }
          },
        },
      ],
    });
  }

  private buildPolicy(formData: any, scopes: TApiTokenScope[]): any | undefined {
    const role = String(formData.policyRole || '').trim();
    if (!role) return undefined;
    const policy: any = {
      role,
      scopes,
    };
    if (role === 'gatewayClient') {
      const type = String(formData.gatewayClientType || 'onebox').trim() as 'onebox' | 'cloudly' | 'custom';
      const id = String(formData.gatewayClientId || '').trim();
      if (id) {
        policy.gatewayClient = { type, id };
      }
      policy.hostnamePatterns = String(formData.hostnamePatterns || '')
        .split(',')
        .map((pattern) => pattern.trim())
        .filter(Boolean);
      const target = String(formData.allowedRouteTarget || '').trim();
      if (target.includes(':')) {
        const [host, portsValue] = target.split(':');
        policy.allowedRouteTargets = [{
          host: host.trim(),
          ports: portsValue.split(',').map((port) => Number(port.trim())).filter((port) => Number.isInteger(port)),
        }];
      }
      policy.capabilities = {
        readDomains: true,
        readDnsRecords: true,
        syncRoutes: true,
        syncDnsRecords: false,
        requestCertificates: false,
      };
    }
    return policy;
  }

  private async showRollTokenDialog(token: interfaces.data.IApiTokenInfo) {
    const { DeesModal } = await import('@design.estate/dees-catalog');

    await DeesModal.createAndShow({
      heading: 'Roll Token Secret',
      content: html`
        <div style="color: #ccc; padding: 8px 0;">
          <p>This will regenerate the secret for <strong>${token.name}</strong>. The old token value will stop working immediately.</p>
        </div>
      `,
      menuOptions: [
        {
          name: 'Cancel',
          iconName: 'lucide:x',
          action: async (modalArg: any) => await modalArg.destroy(),
        },
        {
          name: 'Roll Token',
          iconName: 'lucide:rotateCw',
          action: async (modalArg: any) => {
            await modalArg.destroy();
            try {
              const response = await appstate.rollApiToken(token.id);
              if (response.success && response.tokenValue) {
                await appstate.routeManagementStatePart.dispatchAction(appstate.fetchApiTokensAction, null);

                await DeesModal.createAndShow({
                  heading: 'Token Rolled',
                  content: html`
                    <div style="color: #ccc; padding: 8px 0;">
                      <p>Copy this token now. It will not be shown again.</p>
                      <div style="background: #111; padding: 12px; border-radius: 6px; margin-top: 8px;">
                        <code style="color: #0f8; word-break: break-all; font-size: 13px;">${response.tokenValue}</code>
                      </div>
                    </div>
                  `,
                  menuOptions: [
                    {
                      name: 'Done',
                      iconName: 'lucide:check',
                      action: async (m: any) => await m.destroy(),
                    },
                  ],
                });
              }
            } catch (error) {
              console.error('Failed to roll token:', error);
            }
          },
        },
      ],
    });
  }

  async firstUpdated() {
    await appstate.routeManagementStatePart.dispatchAction(appstate.fetchApiTokensAction, null);
  }
}
