./knowledge-base/frontend/src/components/SearchDialogs.tsx

import { useState, useEffect } from "react";
import {
  Box,
  Typography,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  IconButton,
  Grid,
  Card,
  CardMedia,
  CardContent,
  CardActionArea,
  Chip,
  Divider,
  Button,
  TextField,
  Autocomplete,
  CircularProgress,
  Alert,
  Snackbar,
  FormControl,
  Select,
  MenuItem,
  InputLabel,
  Paper,
} from "@mui/material";
import {
  Close as CloseIcon,
  MenuBook as MenuBookIcon,
  Edit as EditIcon,
  Save as SaveIcon,
  Cancel as CancelIcon,
  ArrowBack as ArrowBackIcon,
} from "@mui/icons-material";
import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query";
import type {
  SearchResultItem,
  ProgramMaster,
  DocumentSummary,
} from "../types";
import type { BookGroup } from "./SearchCards";
import { EraSelector } from "./EraSelector";
import {
  updatePageMetadata,
  updateDocumentMetadata,
  fetchPrograms,
  searchPages,
} from "../api/client";

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

/** BookPagesDialog 内で書籍メタ情報を編集するパネル */
function BookMetaEditPanel({
  doc,
  programs: programMasters,
  onClose,
  onSaved,
}: {
  doc: DocumentSummary;
  programs: ProgramMaster[];
  onClose: () => void;
  onSaved: () => void;
}) {
  const queryClient = useQueryClient();
  const [title, setTitle] = useState(doc.title ?? "");
  const [docGenre, setDocGenre] = useState(doc.document_genre ?? "");
  const [selectedPrograms, setSelectedPrograms] = useState<string[]>(
    doc.programs ?? [],
  );
  const [eraLabels, setEraLabels] = useState<string[]>(doc.era_labels ?? []);
  const [snack, setSnack] = useState<{
    open: boolean;
    msg: string;
    severity: "success" | "error";
  }>({ open: false, msg: "", severity: "success" });

  const saveMutation = useMutation({
    mutationFn: () =>
      updateDocumentMetadata(doc.document_id, {
        title,
        document_genre: docGenre,
        programs: selectedPrograms,
        era_labels: eraLabels,
      }),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["search"] });
      queryClient.invalidateQueries({ queryKey: ["browse"] });
      queryClient.invalidateQueries({ queryKey: ["maintenance-documents"] });
      setSnack({ open: true, msg: "保存しました", severity: "success" });
      onSaved();
    },
    onError: () => {
      setSnack({ open: true, msg: "保存に失敗しました", severity: "error" });
    },
  });

  return (
    <Paper
      variant="outlined"
      sx={{ m: 2, p: 2, display: "flex", flexDirection: "column", gap: 2 }}
    >
      <Typography variant="subtitle2" color="primary" fontWeight="bold">
        書籍メタ情報を編集
      </Typography>
      <Typography variant="body2" color="text.secondary">
        このタイトルに属するすべてのページに一括で適用されます。
      </Typography>

      <TextField
        label="タイトル"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        fullWidth
        size="small"
      />

      <FormControl fullWidth size="small">
        <InputLabel>資料ジャンル</InputLabel>
        <Select
          value={docGenre}
          label="資料ジャンル"
          onChange={(e) => setDocGenre(e.target.value)}
        >
          <MenuItem value="">(未設定)</MenuItem>
          {DOC_GENRE_OPTIONS.map((g) => (
            <MenuItem key={g} value={g}>
              {g}
            </MenuItem>
          ))}
        </Select>
      </FormControl>

      <Autocomplete
        multiple
        disableCloseOnSelect
        options={programMasters.map((p) => p.name)}
        value={selectedPrograms}
        onChange={(_, v) => setSelectedPrograms(v)}
        renderInput={(params) => (
          <TextField {...params} label="番組" size="small" />
        )}
        renderTags={(value, getTagProps) =>
          value.map((option, index) => (
            <Chip
              label={option}
              size="small"
              {...getTagProps({ index })}
              key={option}
            />
          ))
        }
      />

      <Box>
        <Typography variant="body2" color="text.secondary" sx={{ mb: 0.5 }}>
          時代タグ
        </Typography>
        <EraSelector value={eraLabels} onChange={setEraLabels} />
      </Box>

      <Box sx={{ display: "flex", gap: 1, justifyContent: "flex-end" }}>
        <Button
          onClick={onClose}
          startIcon={<CancelIcon />}
          disabled={saveMutation.isPending}
        >
          キャンセル
        </Button>
        <Button
          variant="contained"
          startIcon={
            saveMutation.isPending ? (
              <CircularProgress size={16} />
            ) : (
              <SaveIcon />
            )
          }
          onClick={() => saveMutation.mutate()}
          disabled={saveMutation.isPending}
        >
          {saveMutation.isPending ? "保存中..." : "保存"}
        </Button>
      </Box>

      <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>
    </Paper>
  );
}

export function BookPagesDialog({
  book,
  onClose,
  onPageClick,
}: {
  book: BookGroup | null;
  onClose: () => void;
  onPageClick: (item: SearchResultItem) => void;
}) {
  const [editMode, setEditMode] = useState(false);

  // book が変わったら編集モードをリセット
  useEffect(() => {
    setEditMode(false);
  }, [book]);

  // 書籍の全ページを取得(検索結果は最大100件のため、document_idで全件取得)
  const { data: allPagesData, isLoading: pagesLoading } = useQuery({
    queryKey: ["bookAllPages", book?.document_id],
    queryFn: () =>
      searchPages({
        query: "",
        filters: { document_id: book!.document_id },
        page: 1,
        page_size: 1000,
      }),
    enabled: !!book,
    staleTime: 2 * 60 * 1000,
  });

  // 番組マスタ(編集パネル用)
  const { data: programList = [] } = useQuery<ProgramMaster[]>({
    queryKey: ["programs"],
    queryFn: fetchPrograms,
    staleTime: 5 * 60 * 1000,
    enabled: !!book && editMode,
  });

  if (!book) return null;

  // 全ページ取得できていればそちらを使用(ロード中は空配列)
  const allPages = allPagesData?.results ?? (pagesLoading ? [] : book.pages);
  const sortedPages = [...allPages].sort(
    (a, b) => a.page_number - b.page_number,
  );

  // 書籍の代表ページから DocumentSummary 相当を組み立てる
  const docSummary: DocumentSummary = {
    document_id: book.document_id,
    title: book.title,
    programs: book.thumbnailItem.programs ?? [],
    document_genre: book.thumbnailItem.document_genre ?? "",
    era_labels: book.thumbnailItem.era_labels ?? [],
  };

  return (
    <Dialog
      open={!!book}
      onClose={editMode ? undefined : onClose}
      maxWidth="md"
      fullWidth
      scroll="paper"
    >
      <DialogTitle sx={{ pr: editMode ? 2 : 10 }}>
        <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
          <MenuBookIcon color="primary" />
          <span>{book.title}</span>
        </Box>
        {!editMode && (
          <Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
            全
            {allPagesData
              ? sortedPages.length
              : (book.thumbnailItem.pdf_total_pages ?? sortedPages.length)}
            ページ
          </Typography>
        )}
        {!editMode && (
          <IconButton
            onClick={() => setEditMode(true)}
            sx={{ position: "absolute", right: 44, top: 8 }}
            aria-label="書籍メタ情報を編集"
            title="書籍メタ情報を編集"
          >
            <EditIcon />
          </IconButton>
        )}
        <IconButton
          onClick={onClose}
          sx={{ position: "absolute", right: 8, top: 8 }}
          aria-label="閉じる"
        >
          <CloseIcon />
        </IconButton>
      </DialogTitle>

      {editMode && (
        <BookMetaEditPanel
          doc={docSummary}
          programs={programList}
          onClose={() => setEditMode(false)}
          onSaved={() => setEditMode(false)}
        />
      )}

      <DialogContent dividers>
        {pagesLoading ? (
          <Box sx={{ display: "flex", justifyContent: "center", py: 6 }}>
            <CircularProgress />
          </Box>
        ) : (
          <Grid container spacing={2}>
            {sortedPages.map((item) => (
              <Grid item xs={12} sm={6} md={4} key={item.page_id}>
                <Card sx={{ height: "100%" }}>
                  <CardActionArea
                    onClick={() => !editMode && onPageClick(item)}
                  >
                    <CardMedia
                      component="img"
                      height="150"
                      image={item.thumbnail_url || item.image_url}
                      alt={`p.${item.page_number}`}
                      sx={{ objectFit: "cover" }}
                    />
                    <CardContent>
                      <Typography variant="body2" fontWeight="bold">
                        p.{item.page_number}
                      </Typography>
                      {item.ocr_supplemental_info &&
                        item.ocr_supplemental_info.length > 0 && (
                          <Typography
                            variant="body2"
                            color="text.secondary"
                            sx={{ mt: 0.5 }}
                          >
                            {item.ocr_supplemental_info[0].substring(0, 60)}
                            {item.ocr_supplemental_info[0].length > 60
                              ? "…"
                              : ""}
                          </Typography>
                        )}
                    </CardContent>
                  </CardActionArea>
                </Card>
              </Grid>
            ))}
          </Grid>
        )}
      </DialogContent>
    </Dialog>
  );
}

function SectionTitle({ children }: { children: React.ReactNode }) {
  return (
    <Typography
      variant="subtitle1"
      fontWeight="bold"
      gutterBottom
      sx={{ mt: 1, color: "primary.main" }}
    >
      {children}
    </Typography>
  );
}

function MetaRow({
  label,
  value,
  multiline,
}: {
  label: string;
  value?: React.ReactNode;
  multiline?: boolean;
}) {
  if (!value && value !== 0) return null;
  return (
    <Box
      sx={{
        display: "flex",
        mb: 1,
        gap: 1,
        alignItems: multiline ? "flex-start" : "center",
      }}
    >
      <Typography
        variant="body2"
        color="text.secondary"
        sx={{ minWidth: 120, flexShrink: 0, fontWeight: 500 }}
      >
        {label}