import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, fireEvent, waitFor, cleanup, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import AddIngredientModal from '@/app/dashboard/ingredients/AddIngredientModal';

const mockCategories = [
  { _id: 'cat1', name: 'Dairy' },
  { _id: 'cat2', name: 'Vegetables' },
];
const mockSubcategories = [
  { _id: 'sub1', name: 'Fresh', categoryId: 'cat1' },
  { _id: 'sub2', name: 'Frozen', categoryId: 'cat2' },
];
const mockSuppliers = [
  { _id: 'sup1', name: 'Acme Foods' },
];

function setupFetchMock(overrides: Record<string, unknown> = {}) {
  vi.spyOn(global, 'fetch').mockImplementation(async (input, init) => {
    const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : (input as RequestInfo).toString();
    const method = init?.method ?? 'GET';

    if (/\/api\/ingredients\/[a-zA-Z0-9]+$/.test(url) && method === 'GET') {
      return {
        ok: true,
        json: () => Promise.resolve(overrides.ingredient ?? {}),
      } as Response;
    }

    if (url.endsWith('/api/ingredients') || /\/api\/ingredients\/[a-zA-Z0-9]+$/.test(url)) {
      if (method === 'POST' || method === 'PUT') {
        return {
          ok: overrides.saveOk !== false,
          status: overrides.saveOk === false ? 400 : 201,
          json: () => Promise.resolve(overrides.saveResponse ?? { _id: 'new-id' }),
        } as Response;
      }
    }

    if (url.endsWith('/api/categories')) {
      return { ok: true, json: () => Promise.resolve(overrides.categories ?? mockCategories) } as Response;
    }
    if (url.endsWith('/api/subcategories')) {
      return { ok: true, json: () => Promise.resolve(overrides.subcategories ?? mockSubcategories) } as Response;
    }
    if (url.endsWith('/api/suppliers')) {
      return { ok: true, json: () => Promise.resolve(overrides.suppliers ?? mockSuppliers) } as Response;
    }

    return { ok: true, json: () => Promise.resolve({}) } as Response;
  });
}

function getInputByLabel(container: HTMLElement, labelText: string): HTMLInputElement {
  const labels = container.querySelectorAll('label');
  for (const label of labels) {
    if (label.textContent?.trim() === labelText) {
      const input = label.parentElement?.querySelector('input, select, textarea');
      if (input) return input as HTMLInputElement;
    }
  }
  throw new Error(`Could not find input for label "${labelText}"`);
}

async function fillRequiredFields(container: HTMLElement, user: ReturnType<typeof userEvent.setup>) {
  const nameInput = within(container).getByPlaceholderText('e.g. Organic Butter');
  await user.type(nameInput, 'Test Ingredient');

  const categorySelect = within(container).getByDisplayValue('Select category\u2026');
  fireEvent.change(categorySelect, { target: { value: 'cat1' } });

  await waitFor(() => {
    const subSelects = within(container).getAllByDisplayValue('Select subcategory\u2026');
    expect(subSelects[0]).not.toBeDisabled();
  });
  const subSelect = within(container).getAllByDisplayValue('Select subcategory\u2026')[0];
  fireEvent.change(subSelect, { target: { value: 'sub1' } });

  const quantityInput = getInputByLabel(container, 'Quantity');
  await user.type(quantityInput, '5');

  const unitInput = within(container).getByPlaceholderText('kg, L, pcs\u2026');
  await user.type(unitInput, 'kg');

  const grossInput = getInputByLabel(container, 'Gross Price (\u20AC)');
  await user.type(grossInput, '10');

  const supplierSelect = within(container).getByDisplayValue('Select supplier\u2026');
  fireEvent.change(supplierSelect, { target: { value: 'sup1' } });
}

describe('AddIngredientModal', () => {
  const defaultProps = {
    open: true,
    onClose: vi.fn(),
    onSaved: vi.fn(),
  };

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

  afterEach(() => {
    cleanup();
  });

  it('renders nothing when open=false', () => {
    setupFetchMock();
    const { container } = render(
      <AddIngredientModal open={false} onClose={vi.fn()} onSaved={vi.fn()} />
    );
    expect(container.innerHTML).toBe('');
  });

  it('loads reference data on open', async () => {
    setupFetchMock();
    const { container } = render(<AddIngredientModal {...defaultProps} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });
  });

  it('shows title "Add Ingredient" in add mode', () => {
    setupFetchMock();
    const { container } = render(<AddIngredientModal {...defaultProps} />);
    expect(within(container).getByText('Add Ingredient')).toBeInTheDocument();
  });

  it('shows title "Edit Ingredient" in edit mode', () => {
    setupFetchMock({
      ingredient: {
        _id: 'ing1',
        name: 'Butter',
        categoryId: 'cat1',
        subcategoryId: 'sub1',
        quantity: 10,
        unit: 'kg',
        unitPrice: 5,
        vat: 9,
        supplierId: 'sup1',
      },
    });

    const { container } = render(<AddIngredientModal {...defaultProps} ingredientId="ing1" />);
    expect(within(container).getByText('Edit Ingredient')).toBeInTheDocument();
  });

  it('shows validation error for empty name', async () => {
    setupFetchMock();
    const { container } = render(<AddIngredientModal {...defaultProps} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });

    fireEvent.click(within(container).getByText('Save'));

    await waitFor(() => {
      expect(within(container).getByText('Ingredient name is required.')).toBeInTheDocument();
    });
  });

  it('shows validation error for no category', async () => {
    setupFetchMock();
    const user = userEvent.setup();
    const { container } = render(<AddIngredientModal {...defaultProps} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });

    const nameInput = within(container).getByPlaceholderText('e.g. Organic Butter');
    await user.type(nameInput, 'Test');

    fireEvent.click(within(container).getByText('Save'));

    await waitFor(() => {
      expect(within(container).getByText('Please select a category.')).toBeInTheDocument();
    });
  });

  it('shows validation error for no subcategory', async () => {
    setupFetchMock();
    const user = userEvent.setup();
    const { container } = render(<AddIngredientModal {...defaultProps} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });

    const nameInput = within(container).getByPlaceholderText('e.g. Organic Butter');
    await user.type(nameInput, 'Test');

    fireEvent.change(within(container).getByDisplayValue('Select category\u2026'), { target: { value: 'cat1' } });

    fireEvent.click(within(container).getByText('Save'));

    await waitFor(() => {
      expect(within(container).getByText('Please select a subcategory.')).toBeInTheDocument();
    });
  });

  it('shows validation error for quantity <= 0', async () => {
    setupFetchMock();
    const user = userEvent.setup();
    const { container } = render(<AddIngredientModal {...defaultProps} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });

    await user.type(within(container).getByPlaceholderText('e.g. Organic Butter'), 'Test');

    fireEvent.change(within(container).getByDisplayValue('Select category\u2026'), { target: { value: 'cat1' } });

    await waitFor(() => {
      expect(within(container).getAllByDisplayValue('Select subcategory\u2026')[0]).not.toBeDisabled();
    });
    fireEvent.change(within(container).getAllByDisplayValue('Select subcategory\u2026')[0], { target: { value: 'sub1' } });

    fireEvent.click(within(container).getByText('Save'));

    await waitFor(() => {
      expect(within(container).getByText('Quantity must be greater than 0.')).toBeInTheDocument();
    });
  });

  it('shows validation error for no unit', async () => {
    setupFetchMock();
    const user = userEvent.setup();
    const { container } = render(<AddIngredientModal {...defaultProps} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });

    await user.type(within(container).getByPlaceholderText('e.g. Organic Butter'), 'Test');
    fireEvent.change(within(container).getByDisplayValue('Select category\u2026'), { target: { value: 'cat1' } });

    await waitFor(() => {
      expect(within(container).getAllByDisplayValue('Select subcategory\u2026')[0]).not.toBeDisabled();
    });
    fireEvent.change(within(container).getAllByDisplayValue('Select subcategory\u2026')[0], { target: { value: 'sub1' } });

    await user.type(getInputByLabel(container, 'Quantity'), '5');

    fireEvent.click(within(container).getByText('Save'));

    await waitFor(() => {
      expect(within(container).getByText('Unit is required.')).toBeInTheDocument();
    });
  });

  it('shows validation error for gross price <= 0', async () => {
    setupFetchMock();
    const user = userEvent.setup();
    const { container } = render(<AddIngredientModal {...defaultProps} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });

    await user.type(within(container).getByPlaceholderText('e.g. Organic Butter'), 'Test');
    fireEvent.change(within(container).getByDisplayValue('Select category\u2026'), { target: { value: 'cat1' } });

    await waitFor(() => {
      expect(within(container).getAllByDisplayValue('Select subcategory\u2026')[0]).not.toBeDisabled();
    });
    fireEvent.change(within(container).getAllByDisplayValue('Select subcategory\u2026')[0], { target: { value: 'sub1' } });

    await user.type(getInputByLabel(container, 'Quantity'), '5');
    await user.type(within(container).getByPlaceholderText('kg, L, pcs\u2026'), 'kg');

    fireEvent.click(within(container).getByText('Save'));

    await waitFor(() => {
      expect(within(container).getByText('Gross price must be greater than 0.')).toBeInTheDocument();
    });
  });

  it('shows validation error for no supplier', async () => {
    setupFetchMock();
    const user = userEvent.setup();
    const { container } = render(<AddIngredientModal {...defaultProps} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });

    await user.type(within(container).getByPlaceholderText('e.g. Organic Butter'), 'Test');
    fireEvent.change(within(container).getByDisplayValue('Select category\u2026'), { target: { value: 'cat1' } });

    await waitFor(() => {
      expect(within(container).getAllByDisplayValue('Select subcategory\u2026')[0]).not.toBeDisabled();
    });
    fireEvent.change(within(container).getAllByDisplayValue('Select subcategory\u2026')[0], { target: { value: 'sub1' } });

    await user.type(getInputByLabel(container, 'Quantity'), '5');
    await user.type(within(container).getByPlaceholderText('kg, L, pcs\u2026'), 'kg');
    await user.type(getInputByLabel(container, 'Gross Price (\u20AC)'), '10');

    fireEvent.click(within(container).getByText('Save'));

    await waitFor(() => {
      expect(within(container).getByText('Please select a supplier.')).toBeInTheDocument();
    });
  });

  it('calls onSaved and onClose on successful save', async () => {
    setupFetchMock();
    const user = userEvent.setup();
    const onSaved = vi.fn();
    const onClose = vi.fn();

    const { container } = render(<AddIngredientModal open={true} onClose={onClose} onSaved={onSaved} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });

    await fillRequiredFields(container, user);

    fireEvent.click(within(container).getByText('Save'));

    await waitFor(() => {
      expect(onSaved).toHaveBeenCalledOnce();
      expect(onClose).toHaveBeenCalledOnce();
    });
  });

  it('shows API error on failed save', async () => {
    setupFetchMock({
      saveOk: false,
      saveResponse: { error: 'Server error occurred' },
    });
    const user = userEvent.setup();

    const { container } = render(<AddIngredientModal {...defaultProps} />);

    await waitFor(() => {
      expect(within(container).getByText('Dairy')).toBeInTheDocument();
    });

    await fillRequiredFields(container, user);

    fireEvent.click(within(container).getByText('Save'));

    await waitFor(() => {
      expect(within(container).getByText('Server error occurred')).toBeInTheDocument();
    });
  });

  it('fetches and populates form in edit mode', async () => {
    setupFetchMock({
      ingredient: {
        _id: 'ing1',
        name: 'Butter',
        categoryId: 'cat1',
        subcategoryId: 'sub1',
        quantity: 10,
        unit: 'kg',
        unitPrice: 5,
        vat: 9,
        supplierId: 'sup1',
      },
    });

    const { container } = render(<AddIngredientModal {...defaultProps} ingredientId="ing1" />);

    await waitFor(() => {
      const nameInput = within(container).getByPlaceholderText('e.g. Organic Butter') as HTMLInputElement;
      expect(nameInput.value).toBe('Butter');
    });
  });
});
