import { fireEvent, render, screen } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { SchedulePage } from "../../pages/SchedulePage";

// Mock data
const mockMeds = [
	{
		id: 1,
		name: "Aspirin",
		packCount: 1,
		blistersPerPack: 2,
		pillsPerBlister: 10,
		looseTablets: 5,
		takenBy: ["John"],
		blisters: [{ usage: 1, every: 1, start: "2024-01-01T09:00:00Z" }],
		intakeRemindersEnabled: true,
		notes: "Take with food",
		pillWeightMg: 500,
		imageUrl: null,
		updatedAt: null,
	},
];

// Fixed timestamp for consistent tests
const FIXED_TIMESTAMP = 1706000000000; // Fixed date for testing

const mockCoverageByMed = {
	Aspirin: {
		name: "Aspirin",
		medsLeft: 25,
		daysLeft: 25,
		depletionDate: "2025-02-15",
		depletionTime: FIXED_TIMESTAMP + 25 * 86400000,
		nextDose: null,
	},
};

const mockFutureDays = [
	{
		dateStr: "Mon, Jan 22",
		date: new Date(FIXED_TIMESTAMP),
		isPast: false,
		meds: [
			{
				medName: "Aspirin",
				total: 1,
				doses: [
					{
						id: `1-0-${FIXED_TIMESTAMP}`,
						timeStr: "09:00",
						when: FIXED_TIMESTAMP,
						usage: 1,
						takenBy: ["John"],
						intakeRemindersEnabled: true,
					},
				],
				lastWhen: FIXED_TIMESTAMP,
			},
		],
	},
];

const mockPastDays = [
	{
		dateStr: "Sun, Jan 21",
		date: new Date(FIXED_TIMESTAMP - 86400000),
		isPast: true,
		meds: [
			{
				medName: "Aspirin",
				total: 1,
				doses: [
					{
						id: `1-0-${FIXED_TIMESTAMP - 86400000}`,
						timeStr: "09:00",
						when: FIXED_TIMESTAMP - 86400000,
						usage: 1,
						takenBy: ["John"],
					},
				],
				lastWhen: FIXED_TIMESTAMP - 86400000,
			},
		],
	},
];

// Factory function for mock context
const createMockContext = (overrides = {}) => ({
	meds: [],
	settings: {
		lowStockThreshold: 30,
		criticalStockThreshold: 7,
		expiryWarningDays: 30,
		lowStockDays: 7,
		normalStockDays: 30,
		highStockDays: 90,
	},
	scheduleDays: 30,
	setScheduleDays: vi.fn(),
	showPastDays: false,
	setShowPastDays: vi.fn(),
	pastDays: [],
	futureDays: [],
	takenDoses: new Set(),
	skippedDoses: new Set(),
	dismissedDoses: new Set(),
	markDoseTaken: vi.fn(),
	markDoseSkipped: vi.fn(),
	undoDoseTaken: vi.fn(),
	undoDoseSkipped: vi.fn(),
	coverageByMed: {},
	depletionByMed: {},
	manuallyExpandedDays: new Set(),
	toggleDayCollapse: vi.fn(),
	openUserFilter: vi.fn(),
	isDoseTakenAutomatically: vi.fn(() => false),
	missedPastDoseIds: [],
	...overrides,
});

let mockContextValue = createMockContext();

// Mock the context
vi.mock("../../context", () => ({
	useAppContext: () => mockContextValue,
}));

vi.mock("../../components/Auth", () => ({
	useAuth: () => ({
		user: { id: 1, username: "testuser" },
	}),
}));

describe("SchedulePage", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		localStorage.clear();
		mockContextValue = createMockContext();
	});

	it("renders schedule page", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		// Should render the schedule section
		const section = document.querySelector("section.grid");
		expect(section).toBeInTheDocument();
	});

	it("renders schedule title", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		expect(screen.getByText(/dashboard\.schedules\.title/i)).toBeInTheDocument();
	});

	it("renders day range selector", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		// Should have schedule days select dropdown
		const select = document.querySelector(".schedule-days-select");
		expect(select).toBeInTheDocument();
	});

	it("renders timeline section", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		// Should have timeline div
		const timeline = document.querySelector(".timeline");
		expect(timeline).toBeInTheDocument();
	});

	it("shows empty state when no medications", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		// With no meds, should show the schedule card but with empty timeline
		const card = document.querySelector(".card.schedule-full");
		expect(card).toBeInTheDocument();
	});

	it("renders card head", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const cardHead = document.querySelector(".card-head");
		expect(cardHead).toBeInTheDocument();
	});

	it("renders schedule days options", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const select = document.querySelector(".schedule-days-select");
		const options = select?.querySelectorAll("option");
		expect(options?.length).toBe(3);
	});

	it("has 30, 90, 180 day options", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		expect(screen.getByText(/dashboard\.schedules\.1month/i)).toBeInTheDocument();
		expect(screen.getByText(/dashboard\.schedules\.3months/i)).toBeInTheDocument();
		expect(screen.getByText(/dashboard\.schedules\.6months/i)).toBeInTheDocument();
	});

	it("can change schedule days", () => {
		const setScheduleDays = vi.fn();
		mockContextValue = createMockContext({ setScheduleDays });

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const select = document.querySelector(".schedule-days-select") as HTMLSelectElement;
		expect(select).toBeInTheDocument();

		fireEvent.change(select, { target: { value: "90" } });
		expect(setScheduleDays).toHaveBeenCalledWith(90);
	});
});

describe("SchedulePage structure", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		localStorage.clear();
		mockContextValue = createMockContext();
	});

	it("has heading element", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const heading = document.querySelector("h2");
		expect(heading).toBeInTheDocument();
	});

	it("renders article element", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const article = document.querySelector("article");
		expect(article).toBeInTheDocument();
	});

	it("renders section element", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const section = document.querySelector("section");
		expect(section).toBeInTheDocument();
	});

	it("renders card with correct class", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const card = document.querySelector(".card.schedule-full");
		expect(card).toBeInTheDocument();
	});
});

describe("SchedulePage with medications", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		localStorage.clear();
		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: mockFutureDays,
			coverageByMed: mockCoverageByMed,
			depletionByMed: { Aspirin: Date.now() + 25 * 86400000 },
		});
	});

	it("renders medication in timeline", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		expect(screen.getByText("Aspirin")).toBeInTheDocument();
	});

	it("renders day block", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const dayBlocks = document.querySelectorAll(".day-block");
		expect(dayBlocks.length).toBeGreaterThan(0);
	});

	it("renders dose item", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const doseItems = document.querySelectorAll(".dose-item");
		expect(doseItems.length).toBeGreaterThan(0);
	});

	it("renders take button", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const takeBtn = document.querySelector(".dose-btn.take");
		expect(takeBtn).toBeInTheDocument();
	});

	it("calls markDoseTaken when clicking take button", () => {
		const markDoseTaken = vi.fn();
		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: mockFutureDays,
			coverageByMed: mockCoverageByMed,
			markDoseTaken,
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const takeBtn = document.querySelector(".dose-btn.take");
		if (takeBtn) {
			fireEvent.click(takeBtn);
			expect(markDoseTaken).toHaveBeenCalled();
		}
	});

	it("renders person name for dose", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		expect(screen.getByText("John")).toBeInTheDocument();
	});

	it("calls openUserFilter when clicking person name", () => {
		const openUserFilter = vi.fn();
		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: mockFutureDays,
			coverageByMed: mockCoverageByMed,
			openUserFilter,
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const personName = screen.getByText("John");
		fireEvent.click(personName);
		expect(openUserFilter).toHaveBeenCalledWith("John");
	});

	it("renders pill weight when available", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		// Aspirin has pillWeightMg of 500
		expect(screen.getByText(/500 mg/)).toBeInTheDocument();
	});

	it("renders reminder icon when enabled", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		// Aspirin has intakeRemindersEnabled
		const reminderIcon = document.querySelector(".reminder-icon");
		expect(reminderIcon).toBeInTheDocument();
	});

	it("renders day blocks", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		// Should have day blocks rendered
		const dayBlocks = document.querySelectorAll(".day-block");
		expect(dayBlocks.length).toBeGreaterThan(0);
	});
});

describe("SchedulePage with past days", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		localStorage.clear();
		mockContextValue = createMockContext({
			meds: mockMeds,
			pastDays: mockPastDays,
			futureDays: mockFutureDays,
			coverageByMed: mockCoverageByMed,
			showPastDays: false,
			missedPastDoseIds: [`1-0-${Date.now() - 86400000}-John`], // One missed dose
		});
	});

	it("renders past days toggle when past days exist", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const toggle = document.querySelector(".past-days-toggle");
		expect(toggle).toBeInTheDocument();
	});

	it("shows missed doses warning", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const warning = document.querySelector(".past-days-warning");
		expect(warning).toBeInTheDocument();
	});

	it("toggles past days visibility", () => {
		const setShowPastDays = vi.fn();
		mockContextValue = createMockContext({
			pastDays: mockPastDays,
			showPastDays: false,
			setShowPastDays,
			missedPastDoseIds: [],
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const toggle = document.querySelector(".past-days-toggle");
		if (toggle) {
			fireEvent.click(toggle);
			expect(setShowPastDays).toHaveBeenCalledWith(true);
		}
	});

	it("collapses past days when already expanded", () => {
		const setShowPastDays = vi.fn();
		mockContextValue = createMockContext({
			pastDays: mockPastDays,
			showPastDays: true,
			setShowPastDays,
			missedPastDoseIds: [],
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const toggle = document.querySelector(".past-days-toggle");
		if (toggle) {
			fireEvent.click(toggle);
			expect(setShowPastDays).toHaveBeenCalledWith(false);
		}
	});
});

describe("SchedulePage with expanded past days", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		localStorage.clear();
		mockContextValue = createMockContext({
			meds: mockMeds,
			pastDays: mockPastDays,
			futureDays: mockFutureDays,
			coverageByMed: mockCoverageByMed,
			showPastDays: true,
			manuallyExpandedDays: new Set(["Sun, Jan 21"]),
		});
	});

	it("renders past day blocks when showPastDays is true", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const pastDayBlocks = document.querySelectorAll(".day-block.past");
		expect(pastDayBlocks.length).toBeGreaterThan(0);
	});

	it("renders day divider for past days", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const dividers = document.querySelectorAll(".day-divider");
		expect(dividers.length).toBeGreaterThan(0);
	});

	it("calls toggleDayCollapse when clicking day divider", () => {
		const toggleDayCollapse = vi.fn();
		mockContextValue = createMockContext({
			meds: mockMeds,
			pastDays: mockPastDays,
			showPastDays: true,
			manuallyExpandedDays: new Set(["Sun, Jan 21"]),
			coverageByMed: mockCoverageByMed,
			toggleDayCollapse,
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const divider = document.querySelector(".day-block.past .day-divider.clickable");
		if (divider) {
			fireEvent.click(divider);
			expect(toggleDayCollapse).toHaveBeenCalled();
		}
	});
});

describe("SchedulePage with taken doses", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		localStorage.clear();
		// Match the dose ID format exactly with the mockFutureDays dose
		// Since we can't predict Date.now(), we make the test check if takenDoses works
	});

	it("marks doses as taken in UI", () => {
		// Create consistent timestamp for test
		const timestamp = Date.now();
		const doseId = `1-0-${timestamp}-John`;

		const testFutureDays = [
			{
				dateStr: "Mon, Jan 22",
				date: new Date(timestamp),
				isPast: false,
				meds: [
					{
						medName: "Aspirin",
						total: 1,
						doses: [{ id: `1-0-${timestamp}`, timeStr: "09:00", when: timestamp, usage: 1, takenBy: ["John"] }],
						lastWhen: timestamp,
					},
				],
			},
		];

		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: testFutureDays,
			coverageByMed: mockCoverageByMed,
			takenDoses: new Set([doseId]),
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		// When dose is taken, the undo button should appear
		const undoBtn = document.querySelector(".dose-btn.undo");
		expect(undoBtn).toBeInTheDocument();
	});

	it("calls undoDoseTaken when clicking undo button", () => {
		const undoDoseTaken = vi.fn();
		const timestamp = Date.now();
		const doseId = `1-0-${timestamp}-John`;

		const testFutureDays = [
			{
				dateStr: "Mon, Jan 22",
				date: new Date(timestamp),
				isPast: false,
				meds: [
					{
						medName: "Aspirin",
						total: 1,
						doses: [{ id: `1-0-${timestamp}`, timeStr: "09:00", when: timestamp, usage: 1, takenBy: ["John"] }],
						lastWhen: timestamp,
					},
				],
			},
		];

		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: testFutureDays,
			coverageByMed: mockCoverageByMed,
			takenDoses: new Set([doseId]),
			undoDoseTaken,
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const undoBtn = document.querySelector(".dose-btn.undo");
		if (undoBtn) {
			fireEvent.click(undoBtn);
			expect(undoDoseTaken).toHaveBeenCalled();
		}
	});
});

describe("SchedulePage skip behavior", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		localStorage.clear();
		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: mockFutureDays,
			coverageByMed: mockCoverageByMed,
		});
	});

	it("shows a skip action alongside take for neutral doses", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		expect(document.querySelector(".dose-btn.take")).toBeInTheDocument();
		expect(document.querySelector(".dose-btn.skip")).toBeInTheDocument();
	});

	it("calls markDoseSkipped when clicking skip", () => {
		const markDoseSkipped = vi.fn();
		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: mockFutureDays,
			coverageByMed: mockCoverageByMed,
			markDoseSkipped,
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const skipButton = document.querySelector(".dose-btn.skip");
		expect(skipButton).toBeInTheDocument();

		if (skipButton) {
			fireEvent.click(skipButton);
		}

		expect(markDoseSkipped).toHaveBeenCalledWith(`1-0-${FIXED_TIMESTAMP}-John`);
	});

	it("renders undo skip state for skipped doses", () => {
		const skippedDoseId = `1-0-${FIXED_TIMESTAMP}-John`;
		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: mockFutureDays,
			coverageByMed: mockCoverageByMed,
			skippedDoses: new Set([skippedDoseId]),
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		expect(document.querySelector(".dose-btn.undo.skip")).toBeInTheDocument();
		expect(screen.getByText("John").closest(".dose-person")).toHaveClass("skipped");
	});

	it("calls undoDoseSkipped when clicking undo skip", () => {
		const skippedDoseId = `1-0-${FIXED_TIMESTAMP}-John`;
		const undoDoseSkipped = vi.fn();
		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: mockFutureDays,
			coverageByMed: mockCoverageByMed,
			skippedDoses: new Set([skippedDoseId]),
			undoDoseSkipped,
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const undoSkipButton = document.querySelector(".dose-btn.undo.skip");
		expect(undoSkipButton).toBeInTheDocument();

		if (undoSkipButton) {
			fireEvent.click(undoSkipButton);
		}

		expect(undoDoseSkipped).toHaveBeenCalledWith(skippedDoseId);
	});

	it("does not mark skipped due doses as overdue", () => {
		vi.useFakeTimers();
		const now = new Date("2026-01-22T12:00:00.000Z");
		vi.setSystemTime(now);

		const when = new Date("2026-01-22T09:00:00.000Z").getTime();
		const baseDoseId = `1-0-${when}`;
		const skippedDoseId = `${baseDoseId}-John`;
		const dueDay = [
			{
				dateStr: "Wed, Jan 22",
				date: new Date(now),
				isPast: false,
				meds: [
					{
						medName: "Aspirin",
						total: 1,
						doses: [{ id: baseDoseId, timeStr: "09:00", when, usage: 1, takenBy: ["John"] }],
						lastWhen: when,
					},
				],
			},
		];

		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: dueDay,
			coverageByMed: mockCoverageByMed,
			skippedDoses: new Set([skippedDoseId]),
		});

		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const personRow = screen.getByText("John").closest(".dose-person");
		expect(personRow).toHaveClass("skipped");
		expect(personRow).not.toHaveClass("overdue");

		vi.useRealTimers();
	});
});

describe("SchedulePage with low stock", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		localStorage.clear();
		mockContextValue = createMockContext({
			meds: mockMeds,
			futureDays: mockFutureDays,
			coverageByMed: {
				Aspirin: {
					name: "Aspirin",
					medsLeft: 3,
					daysLeft: 3,
					depletionDate: "2025-01-25",
					depletionTime: Date.now() + 3 * 86400000,
					nextDose: null,
				},
			},
			depletionByMed: { Aspirin: Date.now() + 3 * 86400000 },
		});
	});

	it("shows status tag for medications", () => {
		render(
			<MemoryRouter>
				<SchedulePage />
			</MemoryRouter>
		);

		const tags = document.querySelectorAll(".tag");
		expect(tags.length).toBeGreaterThan(0);
	});
});
