// import React from "react";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import { vi } from "vitest";
import { FileUpload } from "../FileUpload";
import { useAppStore } from "../../../stores/useAppStore";
import * as fileProcessor from "../../../utils/fileProcessor";

// Mock dependencies
vi.mock("../../../stores/useAppStore");

// Mock DragEvent for testing environment
class MockDragEvent extends Event {
  dataTransfer: DataTransfer;

  constructor(
    type: string,
    options: { bubbles?: boolean; dataTransfer?: any } = {},
  ) {
    super(type, { bubbles: options.bubbles });
    this.dataTransfer = options.dataTransfer || {
      items: options.dataTransfer?.items || [],
      files: options.dataTransfer?.files || [],
    };
  }
}

// @ts-ignore
global.DragEvent = MockDragEvent;

const mockUseAppStore = useAppStore as any;

describe("FileUpload", () => {
  const mockSetLoading = vi.fn();
  const mockSetError = vi.fn();
  const mockUploadFile = vi.fn();
  const mockClearFileUploadErrors = vi.fn();
  const mockAddFileUploadError = vi.fn();

  // Mock fileProcessor functions
  const mockProcessExcelFile = vi.fn();
  const mockGetFileErrors = vi.fn();
  const mockFilterValidFiles = vi.fn();

  const defaultStoreState = {
    isLoading: false,
    error: null,
    fileUploadErrors: [],
    currentFile: null,
    setLoading: mockSetLoading,
    setError: mockSetError,
    uploadFile: mockUploadFile,
    clearFileUploadErrors: mockClearFileUploadErrors,
  };

  beforeEach(() => {
    vi.clearAllMocks();
    mockUseAppStore.mockReturnValue(defaultStoreState);
    // @ts-ignore
    mockUseAppStore.getState = vi.fn().mockReturnValue({
      addFileUploadError: mockAddFileUploadError,
    });

    // Mock fileProcessor functions
    vi.spyOn(fileProcessor, "processExcelFile").mockImplementation(
      mockProcessExcelFile,
    );
    vi.spyOn(fileProcessor, "getFileErrors").mockImplementation(
      mockGetFileErrors,
    );
    vi.spyOn(fileProcessor, "filterValidFiles").mockImplementation(
      mockFilterValidFiles,
    );

    // Default mock implementations
    mockGetFileErrors.mockReturnValue([]);
    mockFilterValidFiles.mockReturnValue([]);
  });

  afterEach(() => {
    vi.clearAllMocks();
    vi.restoreAllMocks();
  });

  describe("초기 렌더링", () => {
    it("기본 업로드 UI를 렌더링한다", () => {
      render(<FileUpload />);

      expect(screen.getByText("Excel 파일을 업로드하세요")).toBeInTheDocument();
      expect(screen.getByText(".xlsx, .xls")).toBeInTheDocument();
      expect(
        screen.getByText("파일을 드래그 앤 드롭하거나 클릭하여 업로드"),
      ).toBeInTheDocument();
      expect(screen.getByLabelText("파일 업로드 영역")).toBeInTheDocument();
    });

    it("파일 입력 요소가 올바르게 설정된다", () => {
      render(<FileUpload />);

      const fileInput = screen.getByLabelText("파일 선택");
      expect(fileInput).toBeInTheDocument();
      expect(fileInput).toHaveAttribute("type", "file");
      expect(fileInput).toHaveAttribute("accept", ".xlsx,.xls");
    });
  });

  describe("로딩 상태", () => {
    it("로딩 중일 때 로딩 UI를 표시한다", () => {
      mockUseAppStore.mockReturnValue({
        ...defaultStoreState,
        isLoading: true,
      });

      render(<FileUpload />);

      expect(screen.getByText("파일 처리 중...")).toBeInTheDocument();
      expect(screen.getByText("잠시만 기다려주세요...")).toBeInTheDocument();
    });

    it("로딩 중일 때 파일 업로드 영역이 비활성화된다", () => {
      mockUseAppStore.mockReturnValue({
        ...defaultStoreState,
        isLoading: true,
      });

      render(<FileUpload />);

      const uploadArea = screen.getByLabelText("파일 업로드 영역");
      expect(uploadArea).toHaveAttribute("tabindex", "-1");
    });
  });

  describe("에러 상태", () => {
    it("에러가 있을 때 에러 UI를 표시한다", () => {
      const errorMessage = "파일 업로드에 실패했습니다.";
      mockUseAppStore.mockReturnValue({
        ...defaultStoreState,
        error: errorMessage,
      });

      render(<FileUpload />);

      expect(screen.getByText("업로드 오류")).toBeInTheDocument();
      expect(screen.getByText(errorMessage)).toBeInTheDocument();
    });

    it("파일 업로드 에러 목록을 표시한다", () => {
      const fileUploadErrors = [
        { fileName: "test1.txt", error: "지원되지 않는 파일 형식입니다." },
        { fileName: "test2.pdf", error: "파일 크기가 너무 큽니다." },
      ];

      mockUseAppStore.mockReturnValue({
        ...defaultStoreState,
        fileUploadErrors,
      });

      render(<FileUpload />);

      expect(screen.getByText("파일 업로드 오류:")).toBeInTheDocument();
      expect(screen.getByText("test1.txt")).toBeInTheDocument();
      expect(
        screen.getByText(/지원되지 않는 파일 형식입니다/),
      ).toBeInTheDocument();
      expect(screen.getByText("test2.pdf")).toBeInTheDocument();
      expect(screen.getByText(/파일 크기가 너무 큽니다/)).toBeInTheDocument();
    });
  });

  describe("성공 상태", () => {
    it("파일 업로드 성공 시 성공 UI를 표시한다", () => {
      const currentFile = {
        name: "test.xlsx",
        size: 1024 * 1024, // 1MB
        uploadedAt: new Date(),
      };

      mockUseAppStore.mockReturnValue({
        ...defaultStoreState,
        currentFile,
      });

      render(<FileUpload />);

      expect(screen.getByText("파일 업로드 완료")).toBeInTheDocument();
      expect(screen.getByText("test.xlsx")).toBeInTheDocument();
      expect(screen.getByText("파일 크기: 1.00 MB")).toBeInTheDocument();
      expect(screen.getByText("다른 파일 업로드")).toBeInTheDocument();
    });
  });

  describe("파일 선택", () => {
    it("파일 선택 버튼 클릭 시 파일 입력을 트리거한다", async () => {
      const user = userEvent.setup();
      render(<FileUpload />);

      const uploadArea = screen.getByLabelText("파일 업로드 영역");
      const fileInput = screen.getByLabelText("파일 선택");

      const clickSpy = vi.spyOn(fileInput, "click");

      await user.click(uploadArea);

      expect(clickSpy).toHaveBeenCalled();
    });

    it("키보드 이벤트(Enter)로 파일 선택을 트리거한다", async () => {
      const user = userEvent.setup();
      render(<FileUpload />);

      const uploadArea = screen.getByLabelText("파일 업로드 영역");
      const fileInput = screen.getByLabelText("파일 선택");

      const clickSpy = vi.spyOn(fileInput, "click");

      uploadArea.focus();
      await user.keyboard("{Enter}");

      expect(clickSpy).toHaveBeenCalled();
    });

    it("키보드 이벤트(Space)로 파일 선택을 트리거한다", async () => {
      const user = userEvent.setup();
      render(<FileUpload />);

      const uploadArea = screen.getByLabelText("파일 업로드 영역");
      const fileInput = screen.getByLabelText("파일 선택");

      const clickSpy = vi.spyOn(fileInput, "click");

      uploadArea.focus();
      await user.keyboard(" ");

      expect(clickSpy).toHaveBeenCalled();
    });
  });

  describe("파일 처리", () => {
    it("유효한 파일 업로드 시 파일 처리 함수를 호출한다", async () => {
      const mockFile = new File(["test content"], "test.xlsx", {
        type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      });

      const successResult = {
        success: true,
        data: [{ id: "sheet1", name: "Sheet1", data: [] }],
        fileName: "test.xlsx",
        fileSize: 1024,
      };

      // Mock valid file
      mockFilterValidFiles.mockReturnValue([mockFile]);
      mockGetFileErrors.mockReturnValue([]);
      mockProcessExcelFile.mockResolvedValue(successResult);

      const user = userEvent.setup();
      render(<FileUpload />);

      const fileInput = screen.getByLabelText("파일 선택");

      await user.upload(fileInput, mockFile);

      await waitFor(() => {
        expect(mockProcessExcelFile).toHaveBeenCalledWith(mockFile);
        expect(mockSetLoading).toHaveBeenCalledWith(
          true,
          "파일을 처리하는 중...",
        );
        expect(mockUploadFile).toHaveBeenCalledWith(successResult);
      });
    });

    it("파일 처리 실패 시 에러 처리를 한다", async () => {
      const mockFile = new File(["test content"], "test.xlsx", {
        type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      });

      const errorResult = {
        success: false,
        error: "파일 형식이 올바르지 않습니다.",
        fileName: "test.xlsx",
        fileSize: 1024,
      };

      // Mock valid file but processing fails
      mockFilterValidFiles.mockReturnValue([mockFile]);
      mockGetFileErrors.mockReturnValue([]);
      mockProcessExcelFile.mockResolvedValue(errorResult);

      const user = userEvent.setup();
      render(<FileUpload />);

      const fileInput = screen.getByLabelText("파일 선택");

      await user.upload(fileInput, mockFile);

      await waitFor(() => {
        expect(mockUploadFile).toHaveBeenCalledWith(errorResult);
      });
    });

    it("파일 처리 중 예외 발생 시 에러 처리를 한다", async () => {
      const mockFile = new File(["test content"], "test.xlsx", {
        type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      });

      // Mock valid file but processing throws
      mockFilterValidFiles.mockReturnValue([mockFile]);
      mockGetFileErrors.mockReturnValue([]);
      mockProcessExcelFile.mockRejectedValue(new Error("Unexpected error"));

      const user = userEvent.setup();
      render(<FileUpload />);

      const fileInput = screen.getByLabelText("파일 선택");

      await user.upload(fileInput, mockFile);

      await waitFor(() => {
        expect(mockSetError).toHaveBeenCalledWith(
          "파일 처리 중 예상치 못한 오류가 발생했습니다.",
        );
      });
    });
  });

  describe("드래그 앤 드롭", () => {
    it("드래그 엔터 시 드래그 오버 상태를 활성화한다", async () => {
      render(<FileUpload />);

      const uploadArea = screen.getByLabelText("파일 업로드 영역");

      const dragEnterEvent = new DragEvent("dragenter", {
        bubbles: true,
        dataTransfer: {
          items: [{ kind: "file" }],
        },
      });

      fireEvent(uploadArea, dragEnterEvent);

      // 드래그 오버 상태 확인 (드래그 오버 시 특별한 스타일이 적용됨)
      expect(uploadArea).toHaveClass(
        "border-blue-500",
        "bg-blue-100",
        "scale-105",
      );
    });

    it("드래그 리브 시 드래그 오버 상태를 비활성화한다", async () => {
      render(<FileUpload />);

      const uploadArea = screen.getByLabelText("파일 업로드 영역");

      // 먼저 드래그 엔터
      const dragEnterEvent = new DragEvent("dragenter", {
        bubbles: true,
        dataTransfer: {
          items: [{ kind: "file" }],
        },
      });

      fireEvent(uploadArea, dragEnterEvent);

      // 드래그 리브
      const dragLeaveEvent = new DragEvent("dragleave", {
        bubbles: true,
      });

      fireEvent(uploadArea, dragLeaveEvent);

      expect(uploadArea).toHaveClass("border-gray-300");
    });

    it("파일 드롭 시 파일 처리를 실행한다", async () => {
      const mockFile = new File(["test content"], "test.xlsx", {
        type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      });

      // Mock valid file
      mockFilterValidFiles.mockReturnValue([mockFile]);
      mockGetFileErrors.mockReturnValue([]);

      render(<FileUpload />);

      const uploadArea = screen.getByLabelText("파일 업로드 영역");

      const dropEvent = new DragEvent("drop", {
        bubbles: true,
        dataTransfer: {
          files: [mockFile],
        },
      });

      fireEvent(uploadArea, dropEvent);

      await waitFor(() => {
        expect(mockProcessExcelFile).toHaveBeenCalledWith(mockFile);
      });
    });
  });

  describe("접근성", () => {
    it("ARIA 라벨과 설명이 올바르게 설정된다", () => {
      render(<FileUpload />);

      const uploadArea = screen.getByLabelText("파일 업로드 영역");
      expect(uploadArea).toHaveAttribute(
        "aria-describedby",
        "upload-instructions",
      );
      expect(uploadArea).toHaveAttribute("role", "button");

      const instructions = screen.getByText("최대 50MB까지 업로드 가능");
      expect(instructions).toHaveAttribute("id", "upload-instructions");
    });

    it("로딩 중일 때 접근성 속성이 올바르게 설정된다", () => {
      mockUseAppStore.mockReturnValue({
        ...defaultStoreState,
        isLoading: true,
      });

      render(<FileUpload />);

      const uploadArea = screen.getByLabelText("파일 업로드 영역");
      expect(uploadArea).toHaveAttribute("tabindex", "-1");
    });
  });
});
