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

function parseOptionalPositiveInteger(value: unknown): number | undefined {
  const parsed = typeof value === 'number'
    ? value
    : typeof value === 'string'
      ? parseInt(value.trim(), 10)
      : Number.NaN;
  return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
}

const protectionModeOptions = [
  { key: 'inherit', option: 'Inherit / unset' },
  { key: 'none', option: 'None (clear inherited)' },
  { key: 'custom', option: 'Custom' },
];

const rateLimitExceededOptions = [
  { key: '429', option: 'Return 429' },
  { key: 'challenge', option: 'Browser challenge' },
];

function getDropdownKey(value: unknown): string {
  if (typeof value === 'string') {
    return value;
  }
  if (value && typeof value === 'object' && 'key' in value) {
    const key = (value as { key?: unknown }).key;
    return typeof key === 'string' ? key : '';
  }
  return '';
}

function protectionModeForValue(value: unknown): 'inherit' | 'none' | 'custom' {
  if (value === null) return 'none';
  if (value === undefined) return 'inherit';
  return 'custom';
}

function buildChallengeConfigFromFormData(data: Record<string, unknown>): NonNullable<interfaces.data.IRouteSecurity['challenge']> | undefined {
  const providerId = String(data.challengeProviderId || 'smartchallenge').trim();
  const challengeType = String(data.challengeType || 'wait').trim();
  const ttlSeconds = parseOptionalPositiveInteger(data.challengeTtlSeconds) || 300;
  if (!providerId || !challengeType) {
    alert('Challenge requires Provider ID and Challenge Type.');
    return undefined;
  }
  return {
    providerId,
    challengeType,
    ...(challengeType === 'wait' ? { settings: { waitSeconds: 0, ttlSeconds } } : {}),
    clearance: { ttlSeconds, bindToHost: true },
  };
}

function buildRateLimitFromFormData(data: Record<string, unknown>): interfaces.data.IRouteSecurity['rateLimit'] | null | undefined {
  const mode = getDropdownKey(data.rateLimitMode) || (Boolean(data.rateLimitEnabled) ? 'custom' : 'inherit');
  if (mode === 'inherit') {
    return undefined;
  }
  if (mode === 'none') {
    return null;
  }
  const maxRequests = parseOptionalPositiveInteger(data.rateLimitMaxRequests);
  const window = parseOptionalPositiveInteger(data.rateLimitWindow);
  if (!maxRequests || !window) {
    alert('Rate limit requires positive Max Requests and Window values.');
    return null;
  }
  const rateLimit: NonNullable<interfaces.data.IRouteSecurity['rateLimit']> = {
    enabled: true,
    maxRequests,
    window,
    keyBy: 'ip' as const,
  };
  if (getDropdownKey(data.rateLimitExceededMode) === 'challenge') {
    const challenge = buildChallengeConfigFromFormData(data);
    if (!challenge) return null;
    rateLimit.onExceeded = {
      type: 'challenge',
      challenge,
      clearanceEffect: 'bypass-rate-limit',
    };
  }
  return rateLimit;
}

function buildChallengeFromFormData(data: Record<string, unknown>): interfaces.data.IRouteSecurity['challenge'] | null | undefined {
  const mode = getDropdownKey(data.challengeMode) || 'inherit';
  if (mode === 'inherit') {
    return undefined;
  }
  if (mode === 'none') {
    return null;
  }
  return buildChallengeConfigFromFormData(data) || null;
}

declare global {
  interface HTMLElementTagNameMap {
    'ops-view-sourceprofiles': OpsViewSourceProfiles;
  }
}

@customElement('ops-view-sourceprofiles')
export class OpsViewSourceProfiles extends DeesElement {
  @state()
  accessor profilesState: appstate.IProfilesTargetsState = appstate.profilesTargetsStatePart.getState()!;

  constructor() {
    super();
    const sub = appstate.profilesTargetsStatePart.select().subscribe((newState) => {
      this.profilesState = newState;
    });
    this.rxSubscriptions.push(sub);
  }

  async connectedCallback() {
    await super.connectedCallback();
    await appstate.profilesTargetsStatePart.dispatchAction(appstate.fetchProfilesAndTargetsAction, null);
  }

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

  public render(): TemplateResult {
    const profiles = this.profilesState.profiles;

    const statsTiles: IStatsTile[] = [
      {
        id: 'totalProfiles',
        title: 'Total Profiles',
        type: 'number',
        value: profiles.length,
        icon: 'lucide:shieldCheck',
        description: 'Reusable source profiles',
        color: '#3b82f6',
      },
    ];

    return html`
      <dees-heading level="3">Source Profiles</dees-heading>
      <div class="profilesContainer">
        <dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
        <dees-table
          .heading1=${'Source Profiles'}
          .heading2=${'Reusable source configurations for routes'}
          .data=${profiles}
          .showColumnFilters=${true}
          .displayFunction=${(profile: interfaces.data.ISourceProfile) => ({
            Name: profile.name,
            Description: profile.description || '-',
            'IP Allow List': (profile.security?.ipAllowList || []).join(', ') || '-',
            'IP Block List': (profile.security?.ipBlockList || []).join(', ') || '-',
            'Max Connections': profile.security?.maxConnections ?? '-',
            'Rate Limit': profile.security?.rateLimit === null
              ? 'none'
              : profile.security?.rateLimit?.enabled
                ? `${profile.security.rateLimit.maxRequests}/${profile.security.rateLimit.window}s${profile.security.rateLimit.onExceeded?.type === 'challenge' ? ' -> challenge' : ''}`
                : '-',
            Challenge: profile.security?.challenge === null
              ? 'none'
              : profile.security?.challenge ? `${profile.security.challenge.providerId}/${profile.security.challenge.challengeType}` : '-',
            Extends: (profile.extendsProfiles || []).length > 0
              ? profile.extendsProfiles!.map(id => {
                  const p = profiles.find(pp => pp.id === id);
                  return p ? p.name : id.slice(0, 8);
                }).join(', ')
              : '-',
          })}
          .dataActions=${[
            {
              name: 'Create Profile',
              iconName: 'lucide:plus',
              type: ['header' as const],
              actionFunc: async () => {
                await this.showCreateProfileDialog();
              },
            },
            {
              name: 'Refresh',
              iconName: 'lucide:rotateCw',
              type: ['header' as const],
              actionFunc: async () => {
                await appstate.profilesTargetsStatePart.dispatchAction(appstate.fetchProfilesAndTargetsAction, null);
              },
            },
            {
              name: 'Edit',
              iconName: 'lucide:pencil',
              type: ['inRow', 'contextmenu'] as any,
              actionFunc: async (actionData: any) => {
                const profile = actionData.item as interfaces.data.ISourceProfile;
                await this.showEditProfileDialog(profile);
              },
            },
            {
              name: 'Delete',
              iconName: 'lucide:trash2',
              type: ['inRow', 'contextmenu'] as any,
              actionFunc: async (actionData: any) => {
                const profile = actionData.item as interfaces.data.ISourceProfile;
                await this.deleteProfile(profile);
              },
            },
          ]}
        ></dees-table>
      </div>
    `;
  }

  private async showCreateProfileDialog() {
    const { DeesModal } = await import('@design.estate/dees-catalog');
    DeesModal.createAndShow({
      heading: 'Create Source Profile',
      content: html`
        <dees-form>
          <dees-input-text .key=${'name'} .label=${'Name'} .required=${true}></dees-input-text>
          <dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
          <dees-input-list .key=${'ipAllowList'} .label=${'IP Allow List'} .placeholder=${'Add IP or CIDR...'}></dees-input-list>
          <dees-input-list .key=${'ipBlockList'} .label=${'IP Block List'} .placeholder=${'Add IP or CIDR...'}></dees-input-list>
          <dees-input-text .key=${'maxConnections'} .label=${'Max Connections'}></dees-input-text>
          <dees-input-dropdown .key=${'rateLimitMode'} .label=${'Rate Limit'} .description=${'Default for route bindings. Use none to clear inherited parent defaults.'} .options=${protectionModeOptions} .selectedOption=${protectionModeOptions[0]}></dees-input-dropdown>
          <dees-input-text .key=${'rateLimitMaxRequests'} .label=${'Max Requests'} .description=${'Requests per source IP'}></dees-input-text>
          <dees-input-text .key=${'rateLimitWindow'} .label=${'Window Seconds'}></dees-input-text>
          <dees-input-dropdown .key=${'rateLimitExceededMode'} .label=${'When Rate Limit Is Exceeded'} .description=${'Return 429 or issue the configured browser challenge.'} .options=${rateLimitExceededOptions} .selectedOption=${rateLimitExceededOptions[0]}></dees-input-dropdown>
          <dees-input-dropdown .key=${'challengeMode'} .label=${'Browser Challenge'} .description=${'Default for HTTP-visible route bindings.'} .options=${protectionModeOptions} .selectedOption=${protectionModeOptions[0]}></dees-input-dropdown>
          <dees-input-text .key=${'challengeProviderId'} .label=${'Challenge Provider ID'} .value=${'smartchallenge'}></dees-input-text>
          <dees-input-text .key=${'challengeType'} .label=${'Challenge Type'} .value=${'wait'}></dees-input-text>
          <dees-input-text .key=${'challengeTtlSeconds'} .label=${'Clearance TTL Seconds'} .value=${'300'}></dees-input-text>
        </dees-form>
      `,
      menuOptions: [
        { name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
        {
          name: 'Create',
          action: async (modalArg: any) => {
            const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
            if (!form) return;
            const data = await form.collectFormData();
            const ipAllowList: string[] = Array.isArray(data.ipAllowList) ? data.ipAllowList : [];
            const ipBlockList: string[] = Array.isArray(data.ipBlockList) ? data.ipBlockList : [];
            const maxConnections = parseOptionalPositiveInteger(data.maxConnections);
            const rateLimit = buildRateLimitFromFormData(data);
            if (rateLimit === null && getDropdownKey(data.rateLimitMode) === 'custom') return;
            const challenge = buildChallengeFromFormData(data);
            if (challenge === null && getDropdownKey(data.challengeMode) === 'custom') return;

            await appstate.profilesTargetsStatePart.dispatchAction(appstate.createProfileAction, {
              name: String(data.name),
              description: data.description ? String(data.description) : undefined,
              security: {
                ...(ipAllowList.length > 0 ? { ipAllowList } : {}),
                ...(ipBlockList.length > 0 ? { ipBlockList } : {}),
                ...(maxConnections ? { maxConnections } : {}),
                ...(rateLimit !== undefined ? { rateLimit } : {}),
                ...(challenge !== undefined ? { challenge } : {}),
              },
            });
            modalArg.destroy();
          },
        },
      ],
    });
  }

  private async showEditProfileDialog(profile: interfaces.data.ISourceProfile) {
    const { DeesModal } = await import('@design.estate/dees-catalog');
    const rateLimitMode = protectionModeForValue(profile.security?.rateLimit);
    const rateLimitExceededMode = profile.security?.rateLimit && profile.security.rateLimit !== null && profile.security.rateLimit.onExceeded?.type === 'challenge'
      ? 'challenge'
      : '429';
    const challengeMode = protectionModeForValue(profile.security?.challenge);
    const challengeDefaults = profile.security?.rateLimit && profile.security.rateLimit !== null && profile.security.rateLimit.onExceeded?.type === 'challenge'
      ? profile.security.rateLimit.onExceeded.challenge
      : profile.security?.challenge && profile.security.challenge !== null
        ? profile.security.challenge
        : undefined;
    DeesModal.createAndShow({
      heading: `Edit Profile: ${profile.name}`,
      content: html`
        <dees-form>
          <dees-input-text .key=${'name'} .label=${'Name'} .value=${profile.name}></dees-input-text>
          <dees-input-text .key=${'description'} .label=${'Description'} .value=${profile.description || ''}></dees-input-text>
          <dees-input-list .key=${'ipAllowList'} .label=${'IP Allow List'} .placeholder=${'Add IP or CIDR...'} .value=${profile.security?.ipAllowList || []}></dees-input-list>
          <dees-input-list .key=${'ipBlockList'} .label=${'IP Block List'} .placeholder=${'Add IP or CIDR...'} .value=${profile.security?.ipBlockList || []}></dees-input-list>
          <dees-input-text .key=${'maxConnections'} .label=${'Max Connections'} .value=${String(profile.security?.maxConnections || '')}></dees-input-text>
          <dees-input-dropdown .key=${'rateLimitMode'} .label=${'Rate Limit'} .description=${'Default for route bindings. Use none to clear inherited parent defaults.'} .options=${protectionModeOptions} .selectedOption=${protectionModeOptions.find((option) => option.key === rateLimitMode) || protectionModeOptions[0]}></dees-input-dropdown>
          <dees-input-text .key=${'rateLimitMaxRequests'} .label=${'Max Requests'} .description=${'Requests per source IP'} .value=${String(profile.security?.rateLimit?.maxRequests || '')}></dees-input-text>
          <dees-input-text .key=${'rateLimitWindow'} .label=${'Window Seconds'} .value=${String(profile.security?.rateLimit?.window || '')}></dees-input-text>
          <dees-input-dropdown .key=${'rateLimitExceededMode'} .label=${'When Rate Limit Is Exceeded'} .description=${'Return 429 or issue the configured browser challenge.'} .options=${rateLimitExceededOptions} .selectedOption=${rateLimitExceededOptions.find((option) => option.key === rateLimitExceededMode) || rateLimitExceededOptions[0]}></dees-input-dropdown>
          <dees-input-dropdown .key=${'challengeMode'} .label=${'Browser Challenge'} .description=${'Default for HTTP-visible route bindings.'} .options=${protectionModeOptions} .selectedOption=${protectionModeOptions.find((option) => option.key === challengeMode) || protectionModeOptions[0]}></dees-input-dropdown>
          <dees-input-text .key=${'challengeProviderId'} .label=${'Challenge Provider ID'} .value=${challengeDefaults?.providerId || 'smartchallenge'}></dees-input-text>
          <dees-input-text .key=${'challengeType'} .label=${'Challenge Type'} .value=${challengeDefaults?.challengeType || 'wait'}></dees-input-text>
          <dees-input-text .key=${'challengeTtlSeconds'} .label=${'Clearance TTL Seconds'} .value=${String(challengeDefaults?.clearance?.ttlSeconds || 300)}></dees-input-text>
        </dees-form>
      `,
      menuOptions: [
        { name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
        {
          name: 'Save',
          action: async (modalArg: any) => {
            const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
            if (!form) return;
            const data = await form.collectFormData();
            const ipAllowList: string[] = Array.isArray(data.ipAllowList) ? data.ipAllowList : [];
            const ipBlockList: string[] = Array.isArray(data.ipBlockList) ? data.ipBlockList : [];
            const maxConnections = parseOptionalPositiveInteger(data.maxConnections);
            const rateLimit = buildRateLimitFromFormData(data);
            if (rateLimit === null && getDropdownKey(data.rateLimitMode) === 'custom') return;
            const challenge = buildChallengeFromFormData(data);
            if (challenge === null && getDropdownKey(data.challengeMode) === 'custom') return;

            await appstate.profilesTargetsStatePart.dispatchAction(appstate.updateProfileAction, {
              id: profile.id,
              name: String(data.name),
              description: data.description ? String(data.description) : undefined,
              security: {
                ipAllowList,
                ipBlockList,
                ...(maxConnections ? { maxConnections } : {}),
                ...(rateLimit !== undefined ? { rateLimit } : {}),
                ...(challenge !== undefined ? { challenge } : {}),
              },
            });
            modalArg.destroy();
          },
        },
      ],
    });
  }

  private async deleteProfile(profile: interfaces.data.ISourceProfile) {
    await appstate.profilesTargetsStatePart.dispatchAction(appstate.deleteProfileAction, {
      id: profile.id,
      force: false,
    });

    const currentState = appstate.profilesTargetsStatePart.getState()!;
    if (currentState.error?.includes('in use')) {
      const { DeesModal } = await import('@design.estate/dees-catalog');
      DeesModal.createAndShow({
        heading: 'Profile In Use',
        content: html`<p>${currentState.error} Force delete?</p>`,
        menuOptions: [
          {
            name: 'Force Delete',
            action: async (modalArg: any) => {
              await appstate.profilesTargetsStatePart.dispatchAction(appstate.deleteProfileAction, {
                id: profile.id,
                force: true,
              });
              modalArg.destroy();
            },
          },
          { name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
        ],
      });
    }
  }
}
