import React, { Dispatch } from "react";
import { render, screen, fireEvent, act } from "@testing-library/react-native";
import SettingsComponent from "@/components/Settings";
import { language_matrix } from "@/app/i18n/api";
import { Settings } from "@/app/lib/settings";
import { getDb, migrateDb } from "@/app/lib/db";
import { Knex } from "knex";
import { WhisperFile } from "@/app/lib/whisper";
import { SQLiteDatabase } from "expo-sqlite";

const RENDER_TIME = 1000;

jest.mock("@/app/lib/whisper", () => {
  const originalModule = jest.requireActual("@/app/lib/whisper");

  return {
    ...originalModule,
    WhisperFile: jest.fn().mockImplementation((tag, targetFileName, label, size) => ({
      tag,
      targetFileName,
      label,
      size,
      doesTargetExist: jest.fn(),
      getDownloadStatus: jest.fn(), // Mock other methods as needed
      isDownloadComplete: jest.fn(() => false), // Initially assume download is not complete
      createDownloadResumable: jest.fn().mockResolvedValue({
        startAsync: jest.fn().mockResolvedValue({}),
      }),
    })),
  };
});

jest.mock("expo-file-system", () => {
  const originalModule = jest.requireActual("expo-file-system");

  return {
    ...originalModule,
    File: jest.fn().mockImplementation(() => ({
      bytes: jest.fn(),
      exists: jest.fn(),
    })),
  };
});

jest.mock("@/app/i18n/api", () => {
  const LanguageServer = jest.fn();
  const Translator = jest.fn();

  // Mock the fetchLanguages method to return a predefined language matrix
  LanguageServer.prototype.fetchLanguages = jest.fn(() => ({
    en: {
      code: "en",
      name: "English",
      targets: ["fr", "es"],
    },
    fr: {
      code: "fr",
      name: "French",
      targets: ["en", "es"],
    },
    es: {
      code: "es",
      name: "Spanish",
      targets: ["en", "fr"],
    },
  } as language_matrix));

  // Mock the translate method
  Translator.prototype.translate = jest.fn((text: string, target: string) => {
    return "Hola, como estas?";
  });

  return {
    LanguageServer,
    Translator,
  };
});


describe("SettingsComponent", () => {
  let db: SQLiteDatabase;
  let settings: Settings;

  beforeEach(async () => {
    db = await getDb("development");
    await migrateDb("development");
    settings = new Settings(db);
    jest.spyOn(Settings, 'getDefault').mockResolvedValue(settings);
    await settings.setHostLanguage("en");
    await settings.setLibretranslateBaseUrl("https://example.com");
  });

  afterEach(async () => {
    jest.restoreAllMocks();
    await migrateDb("development", "down");
  });

  beforeAll(async () => {
    jest.useFakeTimers();
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  test("renders correctly with initial settings", async () => {
    render(<SettingsComponent />);
    screen.debug();

    // Wait for the component to fetch and display the initial settings
    await screen.findByText(/Host Language:/i);
    await screen.findByText(/LibreTranslate Base URL:/i);

    // expect(screen.getByDisplayValue("English")).toBeTruthy();
    expect(
      screen.getByAccessibilityHint("libretranslate base url")
    ).toBeTruthy();
  });

  test("updates host language setting when input changes", async () => {
    render(<SettingsComponent />);

    // Wait for the component to fetch and display the initial settings
    await screen.findByText(/Host Language:/i);
    await screen.findByText(/LibreTranslate Base URL:/i);

    // Change the host language input value
    const picker = screen.getByAccessibilityHint("host language");
    fireEvent(picker, "onvalueChange", "es");
    expect(picker.props.selectedIndex).toStrictEqual(0);
  });

  test("updates LibreTranslate base URL setting when input changes", async () => {
    render(<SettingsComponent />);

    jest.advanceTimersByTime(RENDER_TIME);
    screen.debug();

    // Wait for the component to fetch and display the initial settings
    await screen.findByText(/Host Language:/i);
    await screen.findByText(/LibreTranslate Base URL:/i);

    // Change the LibreTranslate base URL input value
    fireEvent.changeText(
      screen.getByAccessibilityHint("libretranslate base url"),
      "http://new-example.com"
    );
    jest.advanceTimersByTime(RENDER_TIME);

    expect(
      screen.getByAccessibilityHint("libretranslate base url")
    ).toBeTruthy();
  });


  describe("Download Whisper Model", () => {
    it("should trigger download when model is not present", async () => {
      const whisperFile = new WhisperFile("small");
      (whisperFile.doesTargetExist as jest.Mock).mockResolvedValue(false);

      render(<SettingsComponent />);
      await screen.findByText(/\s*Download Small\s*/i);
      // Assuming there's a button or trigger to start download
      act(() => {
        fireEvent.press(screen.getByText(/\s*Download Small\s*/i));
      })

      expect(whisperFile.createDownloadResumable).toHaveBeenCalled();
    });

    it("should show progress when download is in progress", async () => {
      const whisperFile = new WhisperFile("small");
      (whisperFile.doesTargetExist as jest.Mock).mockResolvedValue(false);
      (whisperFile.getDownloadStatus as jest.Mock).mockResolvedValue({
        doesTargetExist: false,
        isDownloadComplete: false,
        hasDownloadStarted: true,
        progress: {
          current: 1024,
          total: 2048,
          remaining: 1024,
          percentRemaining: 50,
        },
      });

      render(<SettingsComponent />);
      await screen.findByText(/Host Language:/i);
      fireEvent.press(screen.getByText(/Download Model/i));

      expect(await screen.findByText("50%")).toBeTruthy();
    });

    it("should indicate download is complete", async () => {
      const whisperFile = new WhisperFile("small");
      (whisperFile.doesTargetExist as jest.Mock).mockResolvedValue(false);
      (whisperFile.getDownloadStatus as jest.Mock)
        .mockResolvedValueOnce({
          doesTargetExist: false,
          isDownloadComplete: false,
          hasDownloadStarted: true,
          progress: {
            current: 1024,
            total: 2048,
            remaining: 1024,
            percentRemaining: 50,
          },
        })
        .mockResolvedValueOnce({
          doesTargetExist: true,
          isDownloadComplete: true,
          hasDownloadStarted: false,
          progress: undefined,
        });

      render(<SettingsComponent />);
      await screen.findByText(/Host Language:/i);
      fireEvent.press(screen.getByText(/Download Model/i));

      expect(await screen.findByText("Download Complete")).toBeTruthy();
    });
  });
});
