./knowledge-base/frontend/src/api/client.ts

import axios from "axios";
import { oktaAuth } from "../auth/oktaConfig";
import type {
  SearchRequest,
  SearchResponse,
  UploadRequest,
  UploadResponse,
  ProcessingStatus,
  BulkUploadRequest,
  BulkUploadResponse,
  PdfUploadUrlRequest,
  PdfUploadUrlResponse,
  EraMaster,
  ProgramMaster,
  DocumentSummary,
  BookCountsResponse,
} from "../types";

// API Gateway URLを環境変数から取得
const API_BASE_URL = import.meta.env.VITE_API_GATEWAY_URL || "/api";

const apiClient = axios.create({
  baseURL: API_BASE_URL,
  headers: {
    "Content-Type": "application/json",
  },
  timeout: 30000,
});

// リクエストインターセプター(Bearerトークン付与)
apiClient.interceptors.request.use((config) => {
  const token = oktaAuth.getAccessToken();
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// レスポンスインターセプター(エラーハンドリング)
apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    console.error("API Error:", error);
    return Promise.reject(error);
  },
);

// ========================================
// 検索API
// ========================================

export const searchPages = async (
  params: SearchRequest,
): Promise<SearchResponse> => {
  const response = await apiClient.post<SearchResponse>("/search", params);
  return response.data;
};

// ========================================
// アップロードAPI
// ========================================

export const uploadPage = async (
  params: UploadRequest,
): Promise<UploadResponse> => {
  const response = await apiClient.post<UploadResponse>(
    "/process/upload",
    params,
  );
  return response.data;
};

export const bulkUpload = async (
  params: BulkUploadRequest,
): Promise<BulkUploadResponse> => {
  const response = await apiClient.post<BulkUploadResponse>(
    "/process/bulk",
    params,
  );
  return response.data;
};

/**
 * PDFアップロード用 Presigned URLを取得し、その URLに対して S3 に直接 PUTアップロードする。
 * アップロード後はバックエンドの pdf-splitter Lambda が自動煎起する。
 */
export const uploadPdf = async (
  params: PdfUploadUrlRequest,
  pdfFile: File,
  onProgress?: (percent: number) => void,
): Promise<PdfUploadUrlResponse> => {
  // Step 1: Presigned PUT URLを取得
  const urlResponse = await apiClient.post<PdfUploadUrlResponse>(
    "/process/pdf-upload-url",
    params,
  );
  const { upload_url } = urlResponse.data;

  // Step 2: CloudFront経由でS3にPUT(企業プロキシ対策)
  // ブラウザから s3.amazonaws.com への直接 PUT は i-FILTER 等の企業プロキシでブロックされる。
  // presigned URL のパス・クエリパラメータを同一オリジン(/pdf-upload/...)に付け替えて
  // CloudFront → S3 経由でルーティングし、CORS と i-FILTER の問題を同時に回避する。
  const presignedUrlObj = new URL(upload_url);
  const proxiedUrl = `/pdf-upload${presignedUrlObj.pathname}${presignedUrlObj.search}`;
  await axios.put(proxiedUrl, pdfFile, {
    headers: { "Content-Type": "application/pdf" },
    // baseURL は指定しないため相対URL(同一オリジン)として送信される
    onUploadProgress: (event) => {
      if (onProgress && event.total) {
        onProgress(Math.round((event.loaded / event.total) * 100));
      }
    },
  });

  return urlResponse.data;
};

// ========================================
// ステータス取得API
// ========================================

export const getProcessingStatus = async (
  pageId: string,
): Promise<ProcessingStatus> => {
  const response = await apiClient.get<ProcessingStatus>(
    `/process/status/${pageId}`,
  );
  return response.data;
};

// ========================================
// 時代マスタ取得
// ========================================

const eraListCache: EraMaster[] = [];

export const loadEraMasterList = async (): Promise<EraMaster[]> => {
  // キャッシュがあればそれを返す
  if (eraListCache.length > 0) {
    return eraListCache;
  }

  // CloudFront経由でS3から直接読み込み(相対パス)
  const response = await fetch("/master/era-list.json");
  const data = await response.json();

  eraListCache.push(...data);
  return data;
};

// ========================================
// 番組マスタ取得(S3 + CloudFront)
// ========================================

let programListCache: ProgramMaster[] | null = null;

export const loadProgramMasterList = async (): Promise<ProgramMaster[]> => {
  if (programListCache !== null) {
    return programListCache;
  }
  // CloudFront経由でS3から直接読み込み
  const response = await fetch("/master/programs-list.json");
  const data = await response.json();
  programListCache = data;
  return data;
};

export const invalidateProgramCache = () => {
  programListCache = null;
};

// ========================================
// メンテナンス API
// ========================================

export const fetchPrograms = async (): Promise<ProgramMaster[]> => {
  const response = await apiClient.get<{ programs: ProgramMaster[] }>(
    "/maintenance/programs",
  );
  return response.data.programs;
};

export const createProgram = async (name: string): Promise<ProgramMaster> => {
  const response = await apiClient.post<{ program: ProgramMaster }>(
    "/maintenance/programs",
    { name },
  );
  return response.data.program;
};

export const deleteProgram = async (programId: string): Promise<void> => {
  await apiClient.delete(`/maintenance/programs/${programId}`);
};

export const fetchAllDocuments = async (): Promise<DocumentSummary[]> => {
  const response = await apiClient.get<{ documents: DocumentSummary[] }>(
    "/maintenance/documents",
  );
  return response.data.documents;
};

export const fetchBookCounts = async (): Promise<BookCountsResponse> => {
  const response = await apiClient.get<BookCountsResponse>(
    "/maintenance/book-counts",
  );
  return response.data;
};

export const updateDocumentPrograms = async (
  documentId: string,
  programs: string[],
): Promise<void> => {
  await apiClient.put(`/maintenance/documents/${documentId}/programs`, {
    programs,
  });
};

// ページ単体のメタ情報更新(要件①)
export const updatePageMetadata = async (
  pageId: string,
  data: import("../types").UpdatePageMetadataRequest,
): Promise<void> => {
  await apiClient.put(`/maintenance/pages/${pageId}/metadata`, data);
};

// 書籍全体のメタ情報一括更新(要件②)
export const updateDocumentMetadata = async (
  documentId: string,
  data: import("../types").UpdateDocumentMetadataRequest,
): Promise<void> => {
  await apiClient.put(`/maintenance/documents/${documentId}/metadata`, data);
};

// 書籍全体の AI再解析(要件②)
export const reanalyzeDocument = async (
  documentId: string,
  options?: import("../types").ReanalyzeDocumentRequest,
): Promise<{ queued_pages: number; message: string }> => {
  const response = await apiClient.post<{
    queued_pages: number;
    message: string;
  }>(`/maintenance/documents/${documentId}/reanalyze`, options ?? {});
  return response.data;
};

// 書籍削除(booksTable + processingStatus + OpenSearch + S3 をすべて削除)
export const deleteDocument = async (
  documentId: string,
): Promise<{ document_id: string; deleted: Record<string, number> }> => {
  const response = await apiClient.delete<{
    document_id: string;
    deleted: Record<string, number>;
  }>(`/process/books/${documentId}`);
  return response.data;
};

export default apiClient;