var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
    var _, done = false;
    for (var i = decorators.length - 1; i >= 0; i--) {
        var context = {};
        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
        for (var p in contextIn.access) context.access[p] = contextIn.access[p];
        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
        if (kind === "accessor") {
            if (result === void 0) continue;
            if (result === null || typeof result !== "object") throw new TypeError("Object expected");
            if (_ = accept(result.get)) descriptor.get = _;
            if (_ = accept(result.set)) descriptor.set = _;
            if (_ = accept(result.init)) initializers.unshift(_);
        }
        else if (_ = accept(result)) {
            if (kind === "field") initializers.unshift(_);
            else descriptor[key] = _;
        }
    }
    if (target) Object.defineProperty(target, contextIn.name, descriptor);
    done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
    var useValue = arguments.length > 2;
    for (var i = 0; i < initializers.length; i++) {
        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
    }
    return useValue ? value : void 0;
};
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, } from '@design.estate/dees-element';
let OpsViewApiTokens = (() => {
    let _classDecorators = [customElement('ops-view-apitokens')];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = DeesElement;
    let _routeState_decorators;
    let _routeState_initializers = [];
    let _routeState_extraInitializers = [];
    var OpsViewApiTokens = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _routeState_decorators = [state()];
            __esDecorate(this, null, _routeState_decorators, { kind: "accessor", name: "routeState", static: false, private: false, access: { has: obj => "routeState" in obj, get: obj => obj.routeState, set: (obj, value) => { obj.routeState = value; } }, metadata: _metadata }, _routeState_initializers, _routeState_extraInitializers);
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            OpsViewApiTokens = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #routeState_accessor_storage = __runInitializers(this, _routeState_initializers, {
            mergedRoutes: [],
            warnings: [],
            httpRedirects: [],
            apiTokens: [],
            gatewayClients: [],
            isLoading: false,
            error: null,
            lastUpdated: 0,
        });
        get routeState() { return this.#routeState_accessor_storage; }
        set routeState(value) { this.#routeState_accessor_storage = value; }
        constructor() {
            super();
            __runInitializers(this, _routeState_extraInitializers);
            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);
        }
        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')};
      }
    `,
        ];
        render() {
            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) => ({
                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'],
                    actionRelevancyCheckFunc: (actionData) => !actionData.item.enabled,
                    actionFunc: async (actionData) => {
                        const token = actionData.item;
                        await appstate.routeManagementStatePart.dispatchAction(appstate.toggleApiTokenAction, { id: token.id, enabled: true });
                    },
                },
                {
                    name: 'Disable',
                    iconName: 'lucide:pause',
                    type: ['inRow', 'contextmenu'],
                    actionRelevancyCheckFunc: (actionData) => actionData.item.enabled,
                    actionFunc: async (actionData) => {
                        const token = actionData.item;
                        await appstate.routeManagementStatePart.dispatchAction(appstate.toggleApiTokenAction, { id: token.id, enabled: false });
                    },
                },
                {
                    name: 'Roll',
                    iconName: 'lucide:rotateCw',
                    type: ['inRow', 'contextmenu'],
                    actionFunc: async (actionData) => {
                        const token = actionData.item;
                        await this.showRollTokenDialog(token);
                    },
                },
                {
                    name: 'Revoke',
                    iconName: 'lucide:trash2',
                    type: ['inRow', 'contextmenu'],
                    actionFunc: async (actionData) => {
                        const token = actionData.item;
                        await appstate.routeManagementStatePart.dispatchAction(appstate.revokeApiTokenAction, token.id);
                    },
                },
            ]}
        ></dees-table>
      </div>
    `;
        }
        renderScopePills(scopes) {
            return html `<div style="display: flex; flex-wrap: wrap; gap: 2px;">${scopes.map((s) => html `<span class="scopePill">${s}</span>`)}</div>`;
        }
        renderStatusBadge(token) {
            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>`;
        }
        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) => await modalArg.destroy(),
                    },
                    {
                        name: 'Create',
                        iconName: 'lucide:key',
                        action: async (modalArg) => {
                            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');
                            const rawScopes = tagsInput?.getValue?.() || tagsInput?.value || formData.scopes || [];
                            const scopes = rawScopes
                                .filter((s) => allScopes.includes(s));
                            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) => await m.destroy(),
                                            },
                                        ],
                                    });
                                }
                            }
                            catch (error) {
                                console.error('Failed to create token:', error);
                            }
                        },
                    },
                ],
            });
        }
        buildPolicy(formData, scopes) {
            const role = String(formData.policyRole || '').trim();
            if (!role)
                return undefined;
            const policy = {
                role,
                scopes,
            };
            if (role === 'gatewayClient') {
                const type = String(formData.gatewayClientType || 'onebox').trim();
                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;
        }
        async showRollTokenDialog(token) {
            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) => await modalArg.destroy(),
                    },
                    {
                        name: 'Roll Token',
                        iconName: 'lucide:rotateCw',
                        action: async (modalArg) => {
                            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) => await m.destroy(),
                                            },
                                        ],
                                    });
                                }
                            }
                            catch (error) {
                                console.error('Failed to roll token:', error);
                            }
                        },
                    },
                ],
            });
        }
        async firstUpdated() {
            await appstate.routeManagementStatePart.dispatchAction(appstate.fetchApiTokensAction, null);
        }
        static {
            __runInitializers(_classThis, _classExtraInitializers);
        }
    };
    return OpsViewApiTokens = _classThis;
})();
export { OpsViewApiTokens };
//# sourceMappingURL=data:application/json;base64, 