./knowledge-base/frontend/src/pages/MaintenancePage.tsx

import { useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
  AppBar,
  Toolbar,
  Typography,
  Button,
  Container,
  Box,
  Tabs,
  Tab,
  TextField,
  List,
  ListItem,
  ListItemText,
  ListItemSecondaryAction,
  IconButton,
  CircularProgress,
  Alert,
  Paper,
  Autocomplete,
  Chip,
  Divider,
  Snackbar,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  FormControl,
  Select,
  MenuItem,
  FormControlLabel,
  Checkbox,
} from "@mui/material";
import {
  Home as HomeIcon,
  Delete as DeleteIcon,
  Add as AddIcon,
  Save as SaveIcon,
  Edit as EditIcon,
  Autorenew as AutorenewIcon,
  Cancel as CancelIcon,
} from "@mui/icons-material";
import { useNavigate } from "react-router-dom";
import {
  fetchPrograms,
  createProgram,
  deleteProgram,
  fetchAllDocuments,
  updateDocumentPrograms,
  updateDocumentMetadata,
  reanalyzeDocument,
  deleteDocument,
} from "../api/client";
import type { ProgramMaster, DocumentSummary } from "../types";
import { EraSelector } from "../components/EraSelector";

// タブパネルラッパー
function TabPanel({
  children,
  value,
  index,
}: {
  children: React.ReactNode;
  value: number;
  index: number;
}) {
  return (
    <Box hidden={value !== index} sx={{ pt: 3 }}>
      {value === index && children}
    </Box>
  );
}

// ============================================================
// タブ1: 番組作成
// ============================================================
function ProgramsTab() {
  const queryClient = useQueryClient();
  const [newName, setNewName] = useState("");
  const [snack, setSnack] = useState<{
    open: boolean;
    msg: string;
    severity: "success" | "error";
  }>({
    open: false,
    msg: "",
    severity: "success",
  });

  const {
    data: programs = [],
    isLoading,
    error,
  } = useQuery<ProgramMaster[]>({
    queryKey: ["programs"],
    queryFn: fetchPrograms,
  });

  const createMutation = useMutation({
    mutationFn: (name: string) => createProgram(name),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["programs"] });
      setNewName("");
      setSnack({ open: true, msg: "番組を追加しました", severity: "success" });
    },
    onError: (err: unknown) => {
      const msg =
        (err as { response?: { data?: { message?: string } } }).response?.data
          ?.message || "追加に失敗しました";
      setSnack({ open: true, msg, severity: "error" });
    },
  });

  const deleteMutation = useMutation({
    mutationFn: (id: string) => deleteProgram(id),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["programs"] });
      setSnack({ open: true, msg: "番組を削除しました", severity: "success" });
    },
    onError: () => {
      setSnack({ open: true, msg: "削除に失敗しました", severity: "error" });
    },
  });

  const handleAdd = () => {
    const name = newName.trim();
    if (!name) return;
    createMutation.mutate(name);
  };

  return (
    <Box>
      <Typography variant="h6" gutterBottom>
        番組作成
      </Typography>
      <Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
        番組名テーブルに番組を追加・削除できます。
      </Typography>

      {/* 追加フォーム */}
      <Paper variant="outlined" sx={{ p: 2, mb: 3 }}>
        <Box sx={{ display: "flex", gap: 2, alignItems: "center" }}>
          <TextField
            label="番組名"
            value={newName}
            onChange={(e) => setNewName(e.target.value)}
            onKeyPress={(e) => e.key === "Enter" && handleAdd()}
            placeholder="例: 大河ドラマ〇〇"
            size="small"
            sx={{ flex: 1 }}
          />
          <Button
            variant="contained"
            startIcon={<AddIcon />}
            onClick={handleAdd}
            disabled={!newName.trim() || createMutation.isPending}
          >
            追加
          </Button>
        </Box>
      </Paper>

      {/* 番組一覧 */}
      {isLoading && <CircularProgress />}
      {error && (
        <Alert severity="error">番組一覧の読み込みに失敗しました</Alert>
      )}
      {!isLoading && programs.length === 0 && (
        <Typography color="text.secondary">番組が登録されていません</Typography>
      )}
      <List dense>
        {programs.map((program, idx) => (
          <Box key={program.id}>
            {idx > 0 && <Divider />}
            <ListItem>
              <ListItemText primary={program.name} />
              <ListItemSecondaryAction>
                <IconButton
                  edge="end"
                  aria-label="削除"
                  onClick={() => {
                    if (window.confirm(`「${program.name}」を削除しますか?`)) {
                      deleteMutation.mutate(program.id);
                    }
                  }}
                  disabled={deleteMutation.isPending}
                  size="small"
                  color="error"
                >
                  <DeleteIcon fontSize="small" />
                </IconButton>
              </ListItemSecondaryAction>
            </ListItem>
          </Box>
        ))}
      </List>

      <Snackbar
        open={snack.open}
        autoHideDuration={3000}
        onClose={() => setSnack((s) => ({ ...s, open: false }))}
      >
        <Alert
          severity={snack.severity}
          onClose={() => setSnack((s) => ({ ...s, open: false }))}
        >
          {snack.msg}
        </Alert>
      </Snackbar>
    </Box>
  );
}

// ============================================================
// タブ2: 番組紐づけ
// ============================================================

// 1資料分の番組紐づけ行
function DocumentProgramRow({
  doc,
  programs,
  onSaved,
}: {
  doc: DocumentSummary;
  programs: ProgramMaster[];
  onSaved: (msg: string, ok: boolean) => void;
}) {
  const [selected, setSelected] = useState<string[]>(doc.programs ?? []);
  const [dirty, setDirty] = useState(false);

  const saveMutation = useMutation({
    mutationFn: () => updateDocumentPrograms(doc.document_id, selected),
    onSuccess: () => {
      setDirty(false);
      onSaved("保存しました", true);
    },
    onError: () => {
      onSaved("保存に失敗しました", false);
    },
  });

  return (
    <Paper variant="outlined" sx={{ p: 2, mb: 1.5 }}>
      <Box
        sx={{
          display: "flex",
          alignItems: "center",
          gap: 2,
          flexWrap: "wrap",
        }}
      >
        {/* タイトル */}
        <Typography
          variant="subtitle2"
          fontWeight="bold"
          sx={{ minWidth: 200, flex: "0 0 auto" }}
        >
          {doc.title || "(タイトル未設定)"}
        </Typography>

        {/* 番組マルチ選択 */}
        <Autocomplete
          multiple
          disableCloseOnSelect
          options={programs.map((p) => p.name)}
          value={selected}
          onChange={(_, newValue) => {
            setSelected(newValue);
            setDirty(true);
          }}
          renderInput={(params) => (
            <TextField
              {...params}
              label="番組名"
              size="small"
              placeholder="選択してください"
            />
          )}
          renderTags={(value, getTagProps) =>
            value.map((option, index) => (
              <Chip
                label={option}
                size="small"
                {...getTagProps({ index })}
                key={option}
              />
            ))
          }
          sx={{ flex: 1, minWidth: 250 }}
          size="small"
        />

        {/* 保存ボタン */}
        <Button
          variant={dirty ? "contained" : "outlined"}
          size="small"
          startIcon={<SaveIcon />}
          onClick={() => saveMutation.mutate()}
          disabled={saveMutation.isPending || !dirty}
          sx={{ whiteSpace: "nowrap" }}
        >
          {saveMutation.isPending ? "保存中..." : "保存"}
        </Button>
      </Box>
    </Paper>
  );
}

function DocumentBindingTab() {
  const [snack, setSnack] = useState<{
    open: boolean;
    msg: string;
    severity: "success" | "error";
  }>({
    open: false,
    msg: "",
    severity: "success",
  });

  const {
    data: documents = [],
    isLoading: docsLoading,
    error: docsError,
  } = useQuery<DocumentSummary[]>({
    queryKey: ["maintenance-documents"],
    queryFn: fetchAllDocuments,
  });

  const { data: programs = [], isLoading: progsLoading } = useQuery<
    ProgramMaster[]
  >({
    queryKey: ["programs"],
    queryFn: fetchPrograms,
  });

  const loading = docsLoading || progsLoading;

  return (
    <Box>
      <Typography variant="h6" gutterBottom>
        番組紐づけ
      </Typography>
      <Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
        各資料タイトルに番組名を紐づけます。同じタイトルの全ページに同一の番組名が設定されます。
      </Typography>

      {loading && <CircularProgress />}
      {docsError && (
        <Alert severity="error">資料一覧の読み込みに失敗しました</Alert>
      )}
      {!loading && documents.length === 0 && (
        <Typography color="text.secondary">資料がありません</Typography>
      )}

      {!loading &&
        documents.map((doc) => (
          <DocumentProgramRow
            key={doc.document_id}
            doc={doc}
            programs={programs}
            onSaved={(msg, ok) =>
              setSnack({ open: true, msg, severity: ok ? "success" : "error" })
            }
          />
        ))}

      <Snackbar
        open={snack.open}
        autoHideDuration={3000}
        onClose={() => setSnack((s) => ({ ...s, open: false }))}
      >
        <Alert
          severity={snack.severity}
          onClose={() => setSnack((s) => ({ ...s, open: false }))}
        >
          {snack.msg}
        </Alert>
      </Snackbar>
    </Box>
  );
}

// ============================================================
// タブ3: 書籍のメタ情報編集
// ============================================================

const DOC_GENRE_OPTIONS = [
  "写真集",
  "ノンフィクション小説",
  "辞書",
  "年表",
  "メール・議事録",
];

type EditState = {
  title: string;
  document_genre: string;
  programs: string[];
  era_labels: string[];