import { useState, useEffect } from 'react';
import type { SshConnection } from '../../lib/ssh-types';

interface SshConnectionFormProps {
  /** Existing connection for edit; null for create. */
  existing: SshConnection | null;
  /** True when rendered in admin context — exposes admin-only flags. */
  adminContext: boolean;
  /** Submit handler; receives the request body and returns a Promise. */
  onSubmit: (body: Record<string, unknown>) => Promise<void>;
  onCancel: () => void;
}

type KeypairSource = 'provided' | 'generate';
type GeneratedKeyType = 'ed25519' | 'rsa-4096';

interface FormState {
  label: string;
  host: string;
  port: string;
  username: string;
  keypairSource: KeypairSource;
  generateKeyType: GeneratedKeyType;
  privateKeyPem: string;
  passphrase: string;
  remotePathPrefix: string;
  commandDenyPatterns: string;
  commandAllowPatterns: string;
  allowRemoteUnrestricted: boolean;
  allowPrivateAddresses: boolean;
  reason: string;
}

function initialFromExisting(existing: SshConnection | null): FormState {
  return {
    label: existing?.label ?? '',
    host: existing?.host ?? '',
    port: existing ? String(existing.port) : '22',
    username: existing?.username ?? '',
    keypairSource: 'provided',
    generateKeyType: 'ed25519',
    privateKeyPem: '',
    passphrase: '',
    remotePathPrefix: existing?.remotePathPrefix ?? '/srv/agent',
    commandDenyPatterns: existing?.commandDenyPatterns ?? '',
    commandAllowPatterns: existing?.commandAllowPatterns ?? '',
    allowRemoteUnrestricted: existing?.allowRemoteUnrestricted ?? false,
    allowPrivateAddresses: existing?.allowPrivateAddresses ?? false,
    reason: '',
  };
}

/**
 * Create/edit form for an SSH connection.
 *
 * - Create: `existing === null`, all required fields visible, privateKeyPem required.
 * - Edit: `existing !== null`, privateKeyPem optional (omitted = keep current key).
 * - Admin context shows `allowRemoteUnrestricted` and `allowPrivateAddresses` toggles.
 *   These flags are admin-only at the API layer; user-context renders them omitted.
 * - When `adminContext && isCreate`: also collect `reason` (required ≥ 8 chars) for the
 *   audit row that POST /admin/globals will write. Edit reasons go via PATCH.
 */
export function SshConnectionForm({ existing, adminContext, onSubmit, onCancel }: SshConnectionFormProps) {
  const isCreate = existing === null;
  const [state, setState] = useState<FormState>(() => initialFromExisting(existing));
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    setState(initialFromExisting(existing));
    setError(null);
  }, [existing?.id]);

  function update<K extends keyof FormState>(key: K, value: FormState[K]) {
    setState(prev => ({ ...prev, [key]: value }));
  }

  const portNum = Number(state.port);
  const portValid = Number.isInteger(portNum) && portNum >= 1 && portNum <= 65535;
  const remotePathOk = state.allowRemoteUnrestricted
    ? true
    : (() => {
        const p = state.remotePathPrefix.trim();
        if (p.length === 0) return false;
        // Accept POSIX (`/srv/agent`), Windows drive (`C:\Users\agent`),
        // UNC (`\\server\share`), or no-leading-slash prefixes. Reject any
        // `..` parent-ref segment in either separator.
        return !p.split(/[\\/]/).includes('..');
      })();
  const needsUploadedKey = isCreate && state.keypairSource === 'provided';
  const baseValid =
    state.label.trim().length > 0 &&
    state.host.trim().length > 0 &&
    state.username.trim().length > 0 &&
    portValid &&
    remotePathOk &&
    (!needsUploadedKey || state.privateKeyPem.length > 0);
  const reasonNeeded = adminContext;
  const reasonValid = !reasonNeeded || state.reason.trim().length >= 8;
  const canSubmit = baseValid && reasonValid && !submitting;

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (!canSubmit) return;
    setSubmitting(true);
    setError(null);

    const body: Record<string, unknown> = {
      label: state.label.trim(),
      host: state.host.trim(),
      port: portNum,
      username: state.username.trim(),
    };
    // Keypair handling. Only meaningful at create-time; on edit we never
    // re-key (a separate rotation flow handles that).
    if (isCreate && state.keypairSource === 'generate') {
      body.keypairSource = 'generate';
      body.generateKeyType = state.generateKeyType;
    } else {
      if (state.privateKeyPem.length > 0) {
        body.privateKeyPem = state.privateKeyPem;
      }
      if (state.passphrase.length > 0) {
        body.passphrase = state.passphrase;
      }
    }
    if (state.allowRemoteUnrestricted && adminContext) {
      body.allowRemoteUnrestricted = true;
    } else {
      body.remotePathPrefix = state.remotePathPrefix.trim();
    }
    if (adminContext && state.allowPrivateAddresses) {
      body.allowPrivateAddresses = true;
    }
    if (state.commandDenyPatterns.trim().length > 0) {
      body.commandDenyPatterns = state.commandDenyPatterns.trim();
    } else if (existing?.commandDenyPatterns) {
      body.commandDenyPatterns = '';
    }
    if (state.commandAllowPatterns.trim().length > 0) {
      body.commandAllowPatterns = state.commandAllowPatterns.trim();
    } else if (existing?.commandAllowPatterns) {
      body.commandAllowPatterns = '';
    }
    if (reasonNeeded) {
      body.reason = state.reason.trim();
    }

    try {
      await onSubmit(body);
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <form onSubmit={handleSubmit} className="space-y-3">
      <div className="grid grid-cols-2 gap-3">
        <Field label="Label" required>
          <input
            type="text"
            value={state.label}
            onChange={e => update('label', e.target.value)}
            className={inputCls}
            placeholder="prod-db (任意の表示名)"
            required
          />
        </Field>
        <Field label="Username" required>
          <input
            type="text"
            value={state.username}
            onChange={e => update('username', e.target.value)}
            className={inputCls + ' font-mono'}
            placeholder="agent"
            required
          />
        </Field>
        <Field label="Host" required>
          <input
            type="text"
            value={state.host}
            onChange={e => update('host', e.target.value)}
            className={inputCls + ' font-mono'}
            placeholder="db.example.com"
            required
          />
        </Field>
        <Field label="Port" required>
          <input
            type="number"
            value={state.port}
            onChange={e => update('port', e.target.value)}
            className={inputCls + ' font-mono'}
            min={1}
            max={65535}
            required
          />
        </Field>
      </div>

      {isCreate && (
        <fieldset className="rounded border border-hairline bg-surface/40 p-3 space-y-2">
          <legend className="px-1 text-2xs font-semibold text-slate-600 uppercase tracking-wide">
            鍵の出所
          </legend>
          <label className="flex items-start gap-2 text-xs cursor-pointer">
            <input
              type="radio"
              name="keypairSource"
              value="provided"
              checked={state.keypairSource === 'provided'}
              onChange={() => update('keypairSource', 'provided')}
              className="mt-0.5"
            />
            <span>
              <span className="font-semibold">既存の秘密鍵を貼り付け</span>
              <span className="block text-2xs text-slate-600">
                ローカルで <code className="font-mono">ssh-keygen</code> して作成済みの鍵を upload します。
              </span>
            </span>
          </label>
          <label className="flex items-start gap-2 text-xs cursor-pointer">
            <input
              type="radio"
              name="keypairSource"
              value="generate"
              checked={state.keypairSource === 'generate'}
              onChange={() => update('keypairSource', 'generate')}
              className="mt-0.5"
            />
            <span>
              <span className="font-semibold">Orchestrator で新規生成</span>
              <span className="block text-2xs text-slate-600">
                秘密鍵は Orchestrator が保持し、作成後に公開鍵が表示されます。それを接続先の
                <code className="font-mono"> ~/.ssh/authorized_keys</code> に追加してください。
              </span>
            </span>
          </label>
          {state.keypairSource === 'generate' && (
            <Field label="Key type">
              <select
                value={state.generateKeyType}
                onChange={e => update('generateKeyType', e.target.value as GeneratedKeyType)}
                className={inputCls}
              >
                <option value="ed25519">Ed25519 (recommended, 推奨)</option>
                <option value="rsa-4096">RSA 4096-bit (互換性重視)</option>
              </select>
            </Field>
          )}
        </fieldset>
      )}

      {(!isCreate || state.keypairSource === 'provided') && (
        <>
          <Field
            label={`Private Key (PEM)${isCreate ? '' : '  ←  空欄なら現在のキーを維持'}`}
            required={isCreate && state.keypairSource === 'provided'}
          >
            <textarea
              value={state.privateKeyPem}
              onChange={e => update('privateKeyPem', e.target.value)}
              className={inputCls + ' font-mono h-32 resize-y'}
              placeholder="-----BEGIN OPENSSH PRIVATE KEY-----&#10;...&#10;-----END OPENSSH PRIVATE KEY-----"
              spellCheck={false}
              autoComplete="off"
              required={isCreate && state.keypairSource === 'provided'}
            />
            <p className="text-2xs text-slate-500 mt-1">
              サーバー側で保存時に AES-256-GCM 暗号化されます。SHA1 ベースの RSA キーは検出時に reject されます。
            </p>
          </Field>

          <Field label="Passphrase  ←  キーが encrypted な場合のみ">
            <input
              type="password"
              value={state.passphrase}
              onChange={e => update('passphrase', e.target.value)}
              className={inputCls + ' font-mono'}
              autoComplete="new-password"
            />
          </Field>
        </>
      )}

      {!state.allowRemoteUnrestricted && (
        <Field label="Remote Path Prefix" required>
          <input
            type="text"
            value={state.remotePathPrefix}
            onChange={e => update('remotePathPrefix', e.target.value)}
            className={inputCls + ' font-mono'}
            placeholder="/srv/agent"
            required
          />
          <p className="text-2xs text-slate-500 mt-1">
            ファイル系オペレーション (Upload/Download) はこのプレフィックス配下に限定されます。
          </p>
        </Field>
      )}

      <div className="grid grid-cols-2 gap-3">
        <Field label="Command Deny Patterns (1 行 1 regex)">
          <textarea
            value={state.commandDenyPatterns}
            onChange={e => update('commandDenyPatterns', e.target.value)}
            className={inputCls + ' font-mono h-20 resize-y text-2xs'}
            placeholder="^rm\s+-rf\s+/&#10;^dd\s"
            spellCheck={false}
          />
        </Field>
        <Field label="Command Allow Patterns (空 = 全許可)">
          <textarea
            value={state.commandAllowPatterns}
            onChange={e => update('commandAllowPatterns', e.target.value)}
            className={inputCls + ' font-mono h-20 resize-y text-2xs'}
            placeholder="^psql\s&#10;^ls\b"
            spellCheck={false}
          />
        </Field>
      </div>

      {adminContext && (
        <fieldset className="rounded border border-amber-200 bg-amber-50/50 p-3">
          <legend className="px-1 text-2xs font-semibold text-amber-800 uppercase tracking-wide">
            Admin-only flags
          </legend>
          <label className="flex items-start gap-2 text-xs cursor-pointer">
            <input
              type="checkbox"
              checked={state.allowRemoteUnrestricted}
              onChange={e => update('allowRemoteUnrestricted', e.target.checked)}
              className="mt-0.5"
            />
            <span>
              <span className="font-semibold">Allow remote unrestricted</span>
              <span className="block text-2xs text-slate-600">
                Remote Path Prefix を無効化。本当に必要な場合のみ。
              </span>
            </span>
          </label>
          <label className="flex items-start gap-2 text-xs cursor-pointer mt-2">
            <input
              type="checkbox"
              checked={state.allowPrivateAddresses}
              onChange={e => update('allowPrivateAddresses', e.target.checked)}
              className="mt-0.5"
            />
            <span>
              <span className="font-semibold">Allow private addresses</span>
              <span className="block text-2xs text-slate-600">
                RFC1918 / localhost への接続を許可。SSRF ガードを緩める設定。
              </span>
            </span>
          </label>
        </fieldset>
      )}

      {reasonNeeded && (
        <Field label="Reason (≥ 8 chars; 監査ログに残ります)" required>
          <input
            type="text"
            value={state.reason}
            onChange={e => update('reason', e.target.value)}
            className={inputCls}
            placeholder="新規ステージング用 global 接続を追加"
            required
          />
        </Field>
      )}

      {error && <div className="text-xs text-red-600">{error}</div>}

      <div className="flex items-center justify-end gap-2 pt-2 border-t border-hairline">
        <button
          type="button"
          onClick={onCancel}
          disabled={submitting}
          className="px-3 h-7 text-xs text-slate-700 border border-hairline bg-canvas rounded-md hover:bg-surface disabled:opacity-50"
        >
          キャンセル
        </button>
        <button
          type="submit"
          disabled={!canSubmit}
          className="px-3 h-7 text-xs font-semibold bg-accent text-accent-fg rounded-md hover:bg-accent-deep disabled:opacity-50"
        >
          {submitting ? '保存中…' : isCreate ? '作成' : '更新'}
        </button>
      </div>
    </form>
  );
}

const inputCls = 'w-full text-xs px-2 py-1.5 border border-hairline rounded';

function Field({ label, required, children }: { label: string; required?: boolean; children: React.ReactNode }) {
  return (
    <label className="block">
      <div className="text-2xs font-semibold text-slate-500 uppercase tracking-wide mb-1">
        {label}
        {required && <span className="text-red-500 ml-1">*</span>}
      </div>
      {children}
    </label>
  );
}
