import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { launchTimeEntryForWorkItem } from '../src/lib/timeEntryLauncher';

const getCurrentUser = vi.fn();
vi.mock('@alga-psa/users/actions', () => ({
  getCurrentUser,
}));

const getCurrentTimePeriod = vi.fn();
vi.mock('../src/actions/timePeriodsActions', () => ({
  getCurrentTimePeriod,
}));

const fetchOrCreateTimeSheet = vi.fn();
const saveTimeEntry = vi.fn();
vi.mock('../src/actions/timeEntryActions', () => ({
  fetchOrCreateTimeSheet,
  saveTimeEntry,
}));

const toastError = vi.fn();
vi.mock('react-hot-toast', () => ({
  toast: { error: toastError },
}));

beforeEach(() => {
  getCurrentUser.mockResolvedValue({ user_id: 'user-1' });
  getCurrentTimePeriod.mockResolvedValue({
    period_id: 'period-1',
    start_date: '2026-01-01',
    end_date: '2026-01-31',
  });
  fetchOrCreateTimeSheet.mockResolvedValue({ id: 'sheet-1' });
  saveTimeEntry.mockResolvedValue({});
});

describe('launchTimeEntryForWorkItem', () => {
  it('fetches current time period before opening the dialog', async () => {
    const openDrawer = vi.fn();
    await launchTimeEntryForWorkItem({
      openDrawer,
      closeDrawer: vi.fn(),
      context: {
        workItemId: 'ticket-1',
        workItemType: 'ticket',
        workItemName: 'Ticket 1',
      },
    });

    expect(getCurrentTimePeriod).toHaveBeenCalled();
    expect(openDrawer).toHaveBeenCalled();
  });

  it('creates or fetches a time sheet for the current user and period', async () => {
    await launchTimeEntryForWorkItem({
      openDrawer: vi.fn(),
      closeDrawer: vi.fn(),
      context: {
        workItemId: 'ticket-1',
        workItemType: 'ticket',
        workItemName: 'Ticket 1',
      },
    });

    expect(fetchOrCreateTimeSheet).toHaveBeenCalledWith('user-1', 'period-1');
  });

  it('builds a ticket work item with ticket context', async () => {
    const openDrawer = vi.fn();
    await launchTimeEntryForWorkItem({
      openDrawer,
      closeDrawer: vi.fn(),
      context: {
        workItemId: 'ticket-1',
        workItemType: 'ticket',
        workItemName: 'Ticket 1',
        ticketNumber: 'T-123',
        clientName: 'Acme',
        timeDescription: 'Worked on issue',
      },
    });

    const element = openDrawer.mock.calls[0][0] as React.ReactElement;
    expect(element.props.workItem.work_item_id).toBe('ticket-1');
    expect(element.props.workItem.type).toBe('ticket');
    expect(element.props.workItem.name).toBe('Ticket 1');
    expect(element.props.workItem.ticket_number).toBe('T-123');
    expect(element.props.workItem.client_name).toBe('Acme');
    expect(element.props.workItem.description).toBe('Worked on issue');
  });

  it('builds an interaction work item with interaction context', async () => {
    const openDrawer = vi.fn();
    const start = new Date('2026-02-01T09:00:00Z');
    const end = new Date('2026-02-01T10:00:00Z');

    await launchTimeEntryForWorkItem({
      openDrawer,
      closeDrawer: vi.fn(),
      context: {
        workItemId: 'interaction-1',
        workItemType: 'interaction',
        workItemName: 'Follow-up',
        interactionType: 'Call',
        clientName: 'Globex',
        startTime: start,
        endTime: end,
      },
    });

    const element = openDrawer.mock.calls[0][0] as React.ReactElement;
    expect(element.props.workItem.work_item_id).toBe('interaction-1');
    expect(element.props.workItem.type).toBe('interaction');
    expect(element.props.workItem.interaction_type).toBe('Call');
    expect(element.props.workItem.client_name).toBe('Globex');
    expect(element.props.workItem.startTime).toEqual(start);
    expect(element.props.workItem.endTime).toEqual(end);
  });

  it('builds a project task work item with task context', async () => {
    const openDrawer = vi.fn();

    await launchTimeEntryForWorkItem({
      openDrawer,
      closeDrawer: vi.fn(),
      context: {
        workItemId: 'task-1',
        workItemType: 'project_task',
        workItemName: 'Build feature',
        projectName: 'Project A',
        phaseName: 'Phase 2',
        taskName: 'Build feature',
        serviceId: 'service-1',
        serviceName: 'Implementation',
      },
    });

    const element = openDrawer.mock.calls[0][0] as React.ReactElement;
    expect(element.props.workItem.type).toBe('project_task');
    expect(element.props.workItem.project_name).toBe('Project A');
    expect(element.props.workItem.phase_name).toBe('Phase 2');
    expect(element.props.workItem.task_name).toBe('Build feature');
    expect(element.props.workItem.service_id).toBe('service-1');
    expect(element.props.workItem.service_name).toBe('Implementation');
  });

  it('shows a toast error when no active time period exists', async () => {
    const openDrawer = vi.fn();
    getCurrentTimePeriod.mockResolvedValueOnce(null);

    await launchTimeEntryForWorkItem({
      openDrawer,
      closeDrawer: vi.fn(),
      context: {
        workItemId: 'ticket-1',
        workItemType: 'ticket',
        workItemName: 'Ticket 1',
      },
    });

    expect(toastError).toHaveBeenCalled();
    expect(openDrawer).not.toHaveBeenCalled();
  });

  it('opens TimeEntryDialog in drawer mode with time period and time sheet', async () => {
    const openDrawer = vi.fn();
    await launchTimeEntryForWorkItem({
      openDrawer,
      closeDrawer: vi.fn(),
      context: {
        workItemId: 'ticket-2',
        workItemType: 'ticket',
        workItemName: 'Ticket 2',
      },
    });

    const element = openDrawer.mock.calls[0][0] as React.ReactElement;
    expect(element.props.inDrawer).toBe(true);
    expect(element.props.timePeriod.period_id).toBe('period-1');
    expect(element.props.timeSheetId).toBe('sheet-1');
  });

  it('prefills service information for project tasks', async () => {
    const openDrawer = vi.fn();
    await launchTimeEntryForWorkItem({
      openDrawer,
      closeDrawer: vi.fn(),
      context: {
        workItemId: 'task-2',
        workItemType: 'project_task',
        workItemName: 'Configure service',
        serviceId: 'service-99',
        serviceName: 'Deployment',
      },
    });

    const element = openDrawer.mock.calls[0][0] as React.ReactElement;
    expect(element.props.workItem.service_id).toBe('service-99');
    expect(element.props.workItem.service_name).toBe('Deployment');
  });

  it('saves time entry and closes the drawer on success', async () => {
    const openDrawer = vi.fn();
    const closeDrawer = vi.fn();

    await launchTimeEntryForWorkItem({
      openDrawer,
      closeDrawer,
      context: {
        workItemId: 'ticket-3',
        workItemType: 'ticket',
        workItemName: 'Ticket 3',
      },
    });

    const element = openDrawer.mock.calls[0][0] as React.ReactElement;
    await element.props.onSave({ id: 'entry-1' });

    expect(saveTimeEntry).toHaveBeenCalled();
    expect(closeDrawer).toHaveBeenCalled();
  });

  it('invokes onComplete after successful save', async () => {
    const openDrawer = vi.fn();
    const closeDrawer = vi.fn();
    const onComplete = vi.fn();

    await launchTimeEntryForWorkItem({
      openDrawer,
      closeDrawer,
      onComplete,
      context: {
        workItemId: 'ticket-4',
        workItemType: 'ticket',
        workItemName: 'Ticket 4',
      },
    });

    const element = openDrawer.mock.calls[0][0] as React.ReactElement;
    await element.props.onSave({ id: 'entry-2' });

    expect(onComplete).toHaveBeenCalled();
  });
});
