import * as plugins from '../plugins.js';
import * as appstate from '../appstate.js';
import * as shared from './shared/index.js';
import * as interfaces from '../../ts_interfaces/index.js';

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

@customElement('objst-view-policies')
export class ObjstViewPolicies extends DeesElement {
  @state()
  accessor policiesState: appstate.IPoliciesState = { policies: [] };

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

  async connectedCallback() {
    super.connectedCallback();
    appstate.policiesStatePart.dispatchAction(appstate.fetchPoliciesAction, null);
  }

  public static styles = [
    cssManager.defaultStyles,
    shared.viewHostCss,
  ];

  public render(): TemplateResult {
    return html`
      <objst-sectionheading>Policies</objst-sectionheading>
      <dees-table
        heading1="Named Policies"
        heading2="Create reusable policies and attach them to buckets"
        .data=${this.policiesState.policies}
        .dataName=${'policy'}
        .displayFunction=${(item: any) => ({
          Name: item.name,
          Description: item.description,
          Statements: String(item.statements.length),
          Updated: new Date(item.updatedAt).toLocaleDateString(),
        })}
        .dataActions=${[
          {
            name: 'Create Policy',
            iconName: 'lucide:plus',
            type: ['header'] as any[],
            actionFunc: async () => {
              await this.showCreatePolicyModal();
            },
          },
          {
            name: 'Edit',
            iconName: 'lucide:pencil',
            type: ['inRow'] as any[],
            actionFunc: async (args: any) => {
              await this.showEditPolicyModal(args.item);
            },
          },
          {
            name: 'Buckets',
            iconName: 'lucide:database',
            type: ['inRow'] as any[],
            actionFunc: async (args: any) => {
              await this.showPolicyBucketsModal(args.item);
            },
          },
          {
            name: 'Delete',
            iconName: 'lucide:trash2',
            type: ['inRow', 'contextmenu'] as any[],
            actionFunc: async (args: any) => {
              await this.showDeletePolicyModal(args.item);
            },
          },
        ]}
      ></dees-table>
    `;
  }

  private getDefaultStatements(): interfaces.data.IObjstStatement[] {
    return [
      {
        Sid: 'PublicRead',
        Effect: 'Allow',
        Principal: '*',
        Action: 's3:GetObject',
        Resource: 'arn:aws:s3:::${bucket}/*',
      },
    ];
  }

  private async showCreatePolicyModal(): Promise<void> {
    const defaultJson = JSON.stringify(this.getDefaultStatements(), null, 2);
    await DeesModal.createAndShow({
      heading: 'Create Policy',
      content: html`
        <style>
          .policy-form { display: flex; flex-direction: column; gap: 12px; }
          .policy-form label { font-size: 13px; font-weight: 600; color: ${cssManager.bdTheme('#333', '#ccc')}; }
          .policy-form input, .policy-form textarea {
            width: 100%;
            padding: 8px 12px;
            border: 1px solid ${cssManager.bdTheme('#ddd', '#444')};
            border-radius: 6px;
            background: ${cssManager.bdTheme('#fff', '#111')};
            color: ${cssManager.bdTheme('#333', '#eee')};
            font-size: 14px;
            box-sizing: border-box;
          }
          .policy-form textarea {
            font-family: monospace;
            font-size: 12px;
            min-height: 200px;
            resize: vertical;
          }
          .policy-form input:focus, .policy-form textarea:focus {
            outline: none;
            border-color: ${cssManager.bdTheme('#2196f3', '#64b5f6')};
          }
          .policy-error { color: #ef5350; font-size: 13px; min-height: 20px; }
          .policy-hint { color: ${cssManager.bdTheme('#666', '#999')}; font-size: 12px; }
        </style>
        <div class="policy-form">
          <div>
            <label>Name</label>
            <input type="text" class="policy-name" placeholder="e.g. Public Read Access" />
          </div>
          <div>
            <label>Description</label>
            <input type="text" class="policy-description" placeholder="e.g. Allow anonymous read access to objects" />
          </div>
          <div>
            <label>Statements (JSON array)</label>
            <p class="policy-hint">Use \${bucket} in Resource ARNs — it will be replaced with the actual bucket name when applied.</p>
            <textarea class="policy-statements">${defaultJson}</textarea>
          </div>
          <div class="policy-error"></div>
        </div>
      `,
      menuOptions: [
        { name: 'Cancel', action: async (modal: any) => { modal.destroy(); } },
        {
          name: 'Create',
          action: async (modal: any) => {
            const root = modal.shadowRoot;
            const name = (root.querySelector('.policy-name') as HTMLInputElement)?.value?.trim();
            const description = (root.querySelector('.policy-description') as HTMLInputElement)?.value?.trim();
            const statementsText = (root.querySelector('.policy-statements') as HTMLTextAreaElement)?.value?.trim();
            const errorDiv = root.querySelector('.policy-error') as HTMLElement;

            if (!name) { errorDiv.textContent = 'Name is required.'; return; }
            if (!statementsText) { errorDiv.textContent = 'Statements are required.'; return; }

            let statements: interfaces.data.IObjstStatement[];
            try {
              statements = JSON.parse(statementsText);
              if (!Array.isArray(statements)) throw new Error('Must be an array');
            } catch (err: any) {
              errorDiv.textContent = `Invalid JSON: ${err.message}`;
              return;
            }

            await appstate.policiesStatePart.dispatchAction(appstate.createPolicyAction, {
              name,
              description: description || '',
              statements,
            });
            modal.destroy();
          },
        },
      ],
    });
  }

  private async showEditPolicyModal(policy: interfaces.data.INamedPolicy): Promise<void> {
    const statementsJson = JSON.stringify(policy.statements, null, 2);
    await DeesModal.createAndShow({
      heading: `Edit Policy: ${policy.name}`,
      content: html`
        <style>
          .policy-form { display: flex; flex-direction: column; gap: 12px; }
          .policy-form label { font-size: 13px; font-weight: 600; color: ${cssManager.bdTheme('#333', '#ccc')}; }
          .policy-form input, .policy-form textarea {
            width: 100%;
            padding: 8px 12px;
            border: 1px solid ${cssManager.bdTheme('#ddd', '#444')};
            border-radius: 6px;
            background: ${cssManager.bdTheme('#fff', '#111')};
            color: ${cssManager.bdTheme('#333', '#eee')};
            font-size: 14px;
            box-sizing: border-box;
          }
          .policy-form textarea {
            font-family: monospace;
            font-size: 12px;
            min-height: 200px;
            resize: vertical;
          }
          .policy-form input:focus, .policy-form textarea:focus {
            outline: none;
            border-color: ${cssManager.bdTheme('#2196f3', '#64b5f6')};
          }
          .policy-error { color: #ef5350; font-size: 13px; min-height: 20px; }
          .policy-hint { color: ${cssManager.bdTheme('#666', '#999')}; font-size: 12px; }
        </style>
        <div class="policy-form">
          <div>
            <label>Name</label>
            <input type="text" class="policy-name" .value=${policy.name} />
          </div>
          <div>
            <label>Description</label>
            <input type="text" class="policy-description" .value=${policy.description} />
          </div>
          <div>
            <label>Statements (JSON array)</label>
            <p class="policy-hint">Use \${bucket} in Resource ARNs — it will be replaced with the actual bucket name when applied.</p>
            <textarea class="policy-statements">${statementsJson}</textarea>
          </div>
          <div class="policy-error"></div>
        </div>
      `,
      menuOptions: [
        { name: 'Cancel', action: async (modal: any) => { modal.destroy(); } },
        {
          name: 'Save',
          action: async (modal: any) => {
            const root = modal.shadowRoot;
            const name = (root.querySelector('.policy-name') as HTMLInputElement)?.value?.trim();
            const description = (root.querySelector('.policy-description') as HTMLInputElement)?.value?.trim();
            const statementsText = (root.querySelector('.policy-statements') as HTMLTextAreaElement)?.value?.trim();
            const errorDiv = root.querySelector('.policy-error') as HTMLElement;

            if (!name) { errorDiv.textContent = 'Name is required.'; return; }
            if (!statementsText) { errorDiv.textContent = 'Statements are required.'; return; }

            let statements: interfaces.data.IObjstStatement[];
            try {
              statements = JSON.parse(statementsText);
              if (!Array.isArray(statements)) throw new Error('Must be an array');
            } catch (err: any) {
              errorDiv.textContent = `Invalid JSON: ${err.message}`;
              return;
            }

            await appstate.policiesStatePart.dispatchAction(appstate.updatePolicyAction, {
              policyId: policy.id,
              name,
              description: description || '',
              statements,
            });
            modal.destroy();
          },
        },
      ],
    });
  }

  private async showDeletePolicyModal(policy: interfaces.data.INamedPolicy): Promise<void> {
    await DeesModal.createAndShow({
      heading: 'Delete Policy',
      content: html`
        <p>Are you sure you want to delete policy <strong>${policy.name}</strong>?</p>
        <p style="color: ${cssManager.bdTheme('#666', '#999')}; font-size: 13px;">
          This will also detach the policy from all buckets it is currently applied to.
        </p>
      `,
      menuOptions: [
        { name: 'Cancel', action: async (modal: any) => { modal.destroy(); } },
        {
          name: 'Delete',
          action: async (modal: any) => {
            await appstate.policiesStatePart.dispatchAction(appstate.deletePolicyAction, {
              policyId: policy.id,
            });
            modal.destroy();
          },
        },
      ],
    });
  }

  private async showPolicyBucketsModal(policy: interfaces.data.INamedPolicy): Promise<void> {
    const data = await appstate.getPolicyBuckets(policy.id);
    let modalRef: any = null;

    const renderContent = (attached: string[], available: string[]) => html`
      <style>
        .bucket-lists { display: flex; flex-direction: column; gap: 16px; }
        .bucket-section h4 {
          margin: 0 0 8px 0;
          font-size: 13px;
          font-weight: 600;
          color: ${cssManager.bdTheme('#333', '#ccc')};
        }
        .bucket-item {
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 8px 12px;
          border-radius: 6px;
          background: ${cssManager.bdTheme('#f5f5f5', '#1a1a2e')};
          margin-bottom: 4px;
          font-size: 14px;
          color: ${cssManager.bdTheme('#333', '#eee')};
        }
        .bucket-item button {
          padding: 4px 12px;
          border-radius: 4px;
          border: none;
          cursor: pointer;
          font-size: 12px;
          font-weight: 500;
        }
        .btn-detach {
          background: ${cssManager.bdTheme('#ffebee', '#3e1a1a')};
          color: ${cssManager.bdTheme('#c62828', '#ef5350')};
        }
        .btn-detach:hover { opacity: 0.8; }
        .btn-attach {
          background: ${cssManager.bdTheme('#e3f2fd', '#1a2a3e')};
          color: ${cssManager.bdTheme('#1565c0', '#64b5f6')};
        }
        .btn-attach:hover { opacity: 0.8; }
        .empty-note {
          color: ${cssManager.bdTheme('#999', '#666')};
          font-size: 13px;
          font-style: italic;
          padding: 8px 0;
        }
      </style>
      <div class="bucket-lists">
        <div class="bucket-section">
          <h4>Attached Buckets (${attached.length})</h4>
          ${attached.length > 0
            ? attached.map((bucket) => html`
                <div class="bucket-item">
                  <span>${bucket}</span>
                  <button class="btn-detach" @click=${async (e: Event) => {
                    (e.target as HTMLButtonElement).disabled = true;
                    await appstate.detachPolicyFromBucket(policy.id, bucket);
                    const fresh = await appstate.getPolicyBuckets(policy.id);
                    if (modalRef) {
                      modalRef.content = renderContent(fresh.attachedBuckets, fresh.availableBuckets);
                    }
                  }}>Detach</button>
                </div>
              `)
            : html`<p class="empty-note">No buckets attached to this policy.</p>`}
        </div>
        <div class="bucket-section">
          <h4>Available Buckets (${available.length})</h4>
          ${available.length > 0
            ? available.map((bucket) => html`
                <div class="bucket-item">
                  <span>${bucket}</span>
                  <button class="btn-attach" @click=${async (e: Event) => {
                    (e.target as HTMLButtonElement).disabled = true;
                    await appstate.attachPolicyToBucket(policy.id, bucket);
                    const fresh = await appstate.getPolicyBuckets(policy.id);
                    if (modalRef) {
                      modalRef.content = renderContent(fresh.attachedBuckets, fresh.availableBuckets);
                    }
                  }}>Attach</button>
                </div>
              `)
            : html`<p class="empty-note">All buckets already have this policy.</p>`}
        </div>
      </div>
    `;

    modalRef = await DeesModal.createAndShow({
      heading: `Buckets for: ${policy.name}`,
      content: renderContent(data.attachedBuckets, data.availableBuckets),
      menuOptions: [
        { name: 'Done', action: async (modal: any) => { modal.destroy(); } },
      ],
    });
  }
}
