/**
 * MemoryPanel.tsx — User Folder の memory/ パネル
 *
 * エージェントの永続メモリエントリの一覧・編集・削除。元は
 * Settings → Memory & Learning に同居していたが、memory はユーザー資産
 * なので User Folder へ移設（Settings 側は Reflection タイムラインのみ）。
 */

import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { HelpText } from '../settings/HelpText';

// ── API types ─────────────────────────────────────────────────────────────────

type MemoryType = 'user' | 'feedback' | 'project' | 'reference';

// Mirrors the server's flat shape from `listMemoryEntries` in
// src/user-folder/memory.ts and `GET /api/local/memory/entries` in
// src/bridge/memory-api.ts. If you change this shape, update both.
interface MemoryEntry {
  name: string;
  description: string;
  type: MemoryType;
  body: string;
}

interface MemoryListResponse {
  entries: MemoryEntry[];
  index: string;
}

// ── API helpers ───────────────────────────────────────────────────────────────

async function fetchMemoryEntries(): Promise<MemoryListResponse> {
  const res = await fetch('/api/local/memory/entries');
  if (!res.ok) throw new Error(`memory.fetchFailed:${res.status}`);
  return res.json();
}

async function upsertMemoryEntry(
  name: string,
  payload: { description: string; type: MemoryType; body: string },
): Promise<void> {
  const res = await fetch(`/api/local/memory/entries/${encodeURIComponent(name)}`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
  const data = await res.json().catch(() => ({ error: res.statusText }));
  if (!res.ok) throw new Error(data.error ?? res.statusText);
}

async function deleteMemoryEntry(name: string): Promise<void> {
  const res = await fetch(`/api/local/memory/entries/${encodeURIComponent(name)}`, {
    method: 'DELETE',
  });
  if (!res.ok) {
    const data = await res.json().catch(() => ({ error: res.statusText }));
    throw new Error(data.error ?? res.statusText);
  }
}

// ── Validator rejection code messages ─────────────────────────────────────────

const REJECTION_KEYS: Record<string, string> = {
  rejected_bad_name:          'memory.rejection.badName',
  rejected_bad_description:   'memory.rejection.badDescription',
  rejected_unknown_type:      'memory.rejection.unknownType',
  rejected_bad_body:          'memory.rejection.badBody',
  rejected_body_too_large:    'memory.rejection.bodyTooLarge',
  rejected_bad_request:       'memory.rejection.badRequest',
};

type TFunc = (key: string, opts?: Record<string, unknown>) => string;

function rejectionMessage(t: TFunc, code: string): string {
  const key = REJECTION_KEYS[code];
  return key ? t(key) : code;
}

// ── MemoryEntryModal ──────────────────────────────────────────────────────────

interface EntryFormState {
  name: string;
  description: string;
  type: MemoryType;
  body: string;
}

const MEMORY_TYPES: MemoryType[] = ['user', 'feedback', 'project', 'reference'];

function MemoryEntryModal({
  initial,
  isNew,
  onClose,
  onSaved,
}: {
  initial: EntryFormState;
  isNew: boolean;
  onClose: () => void;
  onSaved: () => void;
}) {
  const { t } = useTranslation('userfolder');
  const [form, setForm] = useState<EntryFormState>(initial);
  const [error, setError] = useState<string | null>(null);
  const [saving, setSaving] = useState(false);

  const set = <K extends keyof EntryFormState>(k: K, v: EntryFormState[K]) =>
    setForm(prev => ({ ...prev, [k]: v }));

  const handleSave = async () => {
    setSaving(true);
    setError(null);
    try {
      await upsertMemoryEntry(form.name, {
        description: form.description,
        type: form.type,
        body: form.body,
      });
      onSaved();
      onClose();
    } catch (e: any) {
      setError(rejectionMessage(t, e.message));
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
      <div className="bg-surface rounded-lg shadow-xl w-full max-w-lg mx-4 flex flex-col max-h-[90vh]">
        <div className="flex items-center justify-between px-4 py-3 border-b border-hairline">
          <h3 className="text-sm font-semibold text-slate-800">
            {isNew ? t('memory.modal.newTitle') : t('memory.modal.editTitle', { name: initial.name })}
          </h3>
          <button
            onClick={onClose}
            className="text-slate-400 hover:text-slate-700 text-lg leading-none"
            aria-label={t('memory.modal.closeAria')}
          >
            ×
          </button>
        </div>

        <div className="overflow-y-auto p-4 space-y-3 flex-1">
          {/* Name — only editable when creating */}
          <div>
            <label className="block text-2xs font-medium text-slate-600 mb-1">
              {t('memory.modal.name')}
              {isNew && <span className="text-slate-400 ml-1">{t('memory.modal.nameHint')}</span>}
            </label>
            <input
              type="text"
              value={form.name}
              onChange={e => set('name', e.target.value)}
              disabled={!isNew}
              placeholder="my-fact"
              className={`w-full h-8 px-2.5 text-[13px] font-mono border border-hairline rounded-md focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none ${
                !isNew ? 'bg-slate-50 text-slate-500 cursor-not-allowed' : 'bg-canvas'
              }`}
            />
          </div>

          <div>
            <label className="block text-2xs font-medium text-slate-600 mb-1">{t('memory.modal.description')}</label>
            <input
              type="text"
              value={form.description}
              onChange={e => set('description', e.target.value)}
              placeholder={t('memory.modal.descriptionPlaceholder')}
              className="w-full h-8 px-2.5 text-[13px] border border-hairline rounded-md focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none"
            />
          </div>

          <div>
            <label className="block text-2xs font-medium text-slate-600 mb-1">{t('memory.modal.type')}</label>
            <select
              value={form.type}
              onChange={e => set('type', e.target.value as MemoryType)}
              className="w-full h-8 px-2 text-[13px] border border-hairline rounded-md focus:ring-2 focus:ring-accent-ring outline-none bg-canvas"
            >
              {MEMORY_TYPES.map(t => (
                <option key={t} value={t}>{t}</option>
              ))}
            </select>
            <HelpText>
              {t('memory.modal.typeHelp')}
            </HelpText>
          </div>

          <div>
            <label className="block text-2xs font-medium text-slate-600 mb-1">{t('memory.modal.body')}</label>
            <textarea
              value={form.body}
              onChange={e => set('body', e.target.value)}
              rows={8}
              className="w-full px-2.5 py-2 text-xs font-mono border border-hairline rounded-md focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none resize-y"
              placeholder={t('memory.modal.bodyPlaceholder')}
            />
          </div>

          {error && (
            <div className="text-xs text-red-600 dark:text-red-300 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 px-3 py-2 rounded-md">
              {error}
            </div>
          )}
        </div>

        <div className="flex justify-end gap-2 px-4 py-3 border-t border-hairline">
          <button
            onClick={onClose}
            className="px-3 h-8 text-xs text-slate-700 border border-hairline bg-canvas rounded-md hover:bg-surface transition-colors"
          >
            {t('memory.modal.cancel')}
          </button>
          <button
            onClick={() => void handleSave()}
            disabled={saving || !form.name.trim() || !form.description.trim()}
            className="px-3 h-8 text-xs font-semibold bg-accent text-accent-fg rounded-md hover:bg-accent-deep disabled:opacity-50 transition-colors"
          >
            {saving ? t('memory.modal.saving') : t('memory.modal.save')}
          </button>
        </div>
      </div>
    </div>
  );
}

// ── MemoryEntriesPanel ────────────────────────────────────────────────────────

function MemoryEntriesPanel() {
  const { t } = useTranslation('userfolder');
  const qc = useQueryClient();
  const { data, isLoading, error } = useQuery<MemoryListResponse>({
    queryKey: ['memory-entries'],
    queryFn: fetchMemoryEntries,
  });

  // Errors thrown by fetchMemoryEntries are "memory.fetchFailed:<status>" sentinels.
  const formatError = (e: unknown): string => {
    const msg = e instanceof Error ? e.message : String(e);
    if (msg.startsWith('memory.fetchFailed:')) {
      return t('memory.fetchFailed', { status: msg.slice('memory.fetchFailed:'.length) });
    }
    return msg;
  };

  const [modal, setModal] = useState<{ entry: EntryFormState; isNew: boolean } | null>(null);
  const [deleting, setDeleting] = useState<string | null>(null);
  const [deleteError, setDeleteError] = useState<string | null>(null);

  const handleNew = () => {
    setModal({
      isNew: true,
      entry: { name: '', description: '', type: 'user', body: '' },
    });
  };

  const handleEdit = (e: MemoryEntry) => {
    setModal({
      isNew: false,
      entry: {
        name: e.name,
        description: e.description,
        type: e.type,
        body: e.body,
      },
    });
  };

  const handleDelete = async (name: string) => {
    if (!confirm(t('memory.confirmDelete', { name }))) return;
    setDeleting(name);
    setDeleteError(null);
    try {
      await deleteMemoryEntry(name);
      await qc.invalidateQueries({ queryKey: ['memory-entries'] });
    } catch (e: any) {
      setDeleteError(t('memory.deleteFailed', { name, error: e.message }));
    } finally {
      setDeleting(null);
    }
  };

  const handleSaved = () => {
    void qc.invalidateQueries({ queryKey: ['memory-entries'] });
  };

  return (
    <div className="rounded-lg border border-hairline">
      <div className="flex items-center justify-between px-4 py-3 border-b border-hairline bg-surface rounded-t-lg">
        <div>
          <h3 className="text-sm font-semibold text-slate-800">{t('memory.entriesTitle')}</h3>
          <p className="text-2xs text-slate-500 mt-0.5">
            {t('memory.entriesSubtitle')}
          </p>
        </div>
        <button
          onClick={handleNew}
          className="px-2.5 h-7 text-2xs font-medium bg-accent text-accent-fg rounded-md hover:bg-accent-deep transition-colors"
        >
          {t('memory.newEntry')}
        </button>
      </div>

      {isLoading && (
        <div className="px-4 py-6 text-xs text-slate-400 text-center">{t('memory.loading')}</div>
      )}

      {error && (
        <div className="px-4 py-3 text-xs text-red-600">
          {t('memory.loadFailed', { error: formatError(error) })}
        </div>
      )}

      {deleteError && (
        <div className="px-4 py-2 text-xs text-red-600 dark:text-red-300 bg-red-50 dark:bg-red-500/15">
          {deleteError}
        </div>
      )}

      {data && data.entries.length === 0 && (
        <div className="px-4 py-8 text-center">
          <p className="text-xs text-slate-400">{t('memory.emptyTitle')}</p>
          <p className="text-2xs text-slate-400 mt-1">
            {t('memory.emptyHint')}
          </p>
        </div>
      )}

      {data && data.entries.length > 0 && (
        <ul className="divide-y divide-hairline">
          {data.entries.map(entry => (
            <li key={entry.name} className="flex items-start gap-3 px-4 py-3 hover:bg-surface/60 transition-colors">
              <div className="flex-1 min-w-0">
                <div className="flex items-center gap-2 flex-wrap">
                  <span className="text-xs font-mono font-medium text-slate-800 truncate">
                    {entry.name}
                  </span>
                  <span className="text-[10px] px-1.5 py-0.5 rounded bg-slate-100 text-slate-500 flex-shrink-0">
                    {entry.type}
                  </span>
                </div>
                <p className="text-2xs text-slate-500 mt-0.5 truncate">{entry.description}</p>
                {entry.body && (
                  <p className="text-2xs text-slate-400 mt-0.5 line-clamp-2 font-mono whitespace-pre-wrap break-words">
                    {entry.body.slice(0, 200)}{entry.body.length > 200 ? '…' : ''}
                  </p>
                )}
              </div>
              <div className="flex gap-1.5 flex-shrink-0 mt-0.5">
                <button
                  onClick={() => handleEdit(entry)}
                  className="px-2 h-6 text-2xs text-slate-600 border border-hairline bg-canvas hover:bg-surface rounded transition-colors"
                >
                  {t('memory.edit')}
                </button>
                <button
                  onClick={() => void handleDelete(entry.name)}
                  disabled={deleting === entry.name}
                  className="px-2 h-6 text-2xs text-red-700 dark:text-red-300 border border-red-200 bg-canvas hover:bg-red-50 dark:hover:bg-red-500/15 rounded transition-colors disabled:opacity-50"
                >
                  {deleting === entry.name ? '…' : t('memory.delete')}
                </button>
              </div>
            </li>
          ))}
        </ul>
      )}

      {modal && (
        <MemoryEntryModal
          initial={modal.entry}
          isNew={modal.isNew}
          onClose={() => setModal(null)}
          onSaved={handleSaved}
        />
      )}
    </div>
  );
}

// ── MemoryPanel (User Folder root) ───────────────────────────────────────────

export function MemoryPanel() {
  const { t } = useTranslation('userfolder');
  return (
    <div className="h-full overflow-y-auto">
      <div className="max-w-3xl mx-auto px-6 py-8 space-y-4">
        <div>
          <h2 className="text-base font-semibold text-slate-900 mb-1">{t('memory.title')}</h2>
          <p className="text-[13px] text-slate-500 leading-relaxed">
            {t('memory.intro')}
          </p>
        </div>
        <MemoryEntriesPanel />
      </div>
    </div>
  );
}
