import { expect, Page } from '@playwright/test';
import { PLAYWRIGHT_BASE_URL } from '../playwright/config';
import { test } from '../playwright/fixtures';



async function goToTimeOverview(page: Page) {
    await page.goto(PLAYWRIGHT_BASE_URL + '/time');
}

async function goToReporting(page: Page) {
    await page.goto(PLAYWRIGHT_BASE_URL + '/reporting');
}

async function goToReportingDetailed(page: Page) {
    await page.goto(PLAYWRIGHT_BASE_URL + '/reporting/detailed');
}

async function createTimeEntryWithProject(page: Page, projectName: string, duration: string) {
    // First create the project through the Projects page
    await page.goto(PLAYWRIGHT_BASE_URL + '/projects');
    await page.getByRole('button', { name: 'Create Project' }).click();
    await page.getByLabel('Project Name').fill(projectName);
    await page.getByRole('dialog').getByRole('button', { name: 'Create Project' }).click();

    // Wait for the project to be created and visible in the list
    await page.getByText(projectName).waitFor({ state: 'visible' });

    // Then create the time entry
    await goToTimeOverview(page);
    await page.getByRole('button', { name: 'Manual time entry' }).click();

    // Fill in the time entry details
    await page.getByTestId('time_entry_description').fill(`Time entry for ${projectName}`);

    await page.getByRole('button', { name: 'No Project' }).click();
    await page.getByText(projectName).click();

    // Set duration
    await page.locator('[role="dialog"] input[name="Duration"]').fill(duration);
    await page.locator('[role="dialog"] input[name="Duration"]').press('Tab');

    // Submit the time entry
    await Promise.all([
        page.getByRole('button', { name: 'Create Time Entry' }).click(),
        page.waitForResponse(response => response.url().includes('/time-entries') && response.status() === 201)
    ]);
}

async function createTimeEntryWithTag(page: Page, tagName: string, duration: string) {
    await goToTimeOverview(page);
    await page.getByRole('button', { name: 'Manual time entry' }).click();

    // Fill in the time entry details
    await page.getByTestId('time_entry_description').fill(`Time entry with tag ${tagName}`);

    // Add tag
    await page.getByRole('button', { name: 'Tags' }).click();
    await page.getByText('Create new tag').click();
    await page.getByPlaceholder('Tag Name').fill(tagName);
    await page.getByRole('button', { name: 'Create Tag' }).click();
    await page.waitForLoadState('networkidle');

    // Set duration
    await page.locator('[role="dialog"] input[name="Duration"]').fill(duration);
    await page.locator('[role="dialog"] input[name="Duration"]').press('Tab');

    // Submit the time entry
    await page.getByRole('button', { name: 'Create Time Entry' }).click();
}

async function createTimeEntryWithBillableStatus(page: Page, isBillable: boolean, duration: string) {
    await goToTimeOverview(page);
    await page.getByRole('button', { name: 'Manual time entry' }).click();

    // Fill in the time entry details
    await page.getByTestId('time_entry_description').fill(`Time entry ${isBillable ? 'billable' : 'non-billable'}`);

    // Set billable status
    await page.getByRole('button', { name: 'Non-Billable' }).click();
    if (!isBillable) {
        await page.getByRole('option', { name: 'Non Billable', exact: true }).click();
    } else {
        await page.getByRole('option', { name: 'Billable', exact: true }).click();
    }

    // Set duration
    await page.locator('[role="dialog"] input[name="Duration"]').fill(duration);
    await page.locator('[role="dialog"] input[name="Duration"]').press('Tab');

    // Submit the time entry
    await page.getByRole('button', { name: 'Create Time Entry' }).click();
}

test('test that project filtering works in reporting', async ({ page }) => {
    const project1 = 'Test Project 1 ' + Math.floor(Math.random() * 10000);
    const project2 = 'Test Project 2 ' + Math.floor(Math.random() * 10000);

    // Create time entries for both projects
    await createTimeEntryWithProject(page, project1, '1h');
    await createTimeEntryWithProject(page, project2, '2h');

    // Go to reporting and filter by project1
    await goToReporting(page);
    await page.getByRole('button', { name: 'Project' }).nth(0).click();
    await page.getByText(project1).click();

    await Promise.all([
        // escape
        page.keyboard.press('Escape'),
        // wait for API request to finish
        page.waitForResponse(response => response.url().includes('/time-entries/aggregate') && response.status() === 200)
    ]);
    await page.waitForLoadState('networkidle');

    // Verify only project1 time entries are shown
    await expect(page.getByText(project1)).toBeVisible();
    await expect(page.getByText(project2)).not.toBeVisible();
});

test('test that tag filtering works in reporting', async ({ page }) => {
    const tag1 = 'Test Tag 1 ' + Math.floor(Math.random() * 10000);
    const tag2 = 'Test Tag 2 ' + Math.floor(Math.random() * 10000);

    // Create time entries with different tags
    await createTimeEntryWithTag(page, tag1, '1h');
    await createTimeEntryWithTag(page, tag2, '2h');

    // Go to reporting and filter by tag1
    await goToReporting(page);
    // wait for all requests to finish
    await page.waitForLoadState('networkidle');

    await page.getByRole('button', { name: 'Tags' }).click();
    await page.getByText(tag1).click();

    await Promise.all([
        // escape
        page.keyboard.press('Escape'),
        // wait for API request to finish
        page.waitForResponse(response => response.url().includes('/time-entries/aggregate') && response.status() === 200)
    ]);

    // Verify only time entries with tag1 are shown
    await expect(page.getByText('1h 00min').first()).toBeVisible();
});

test('test that billable status filtering works in reporting', async ({ page }) => {
    // Create billable and non-billable time entries
    await createTimeEntryWithBillableStatus(page, true, '1h');
    await createTimeEntryWithBillableStatus(page, false, '2h');

    // Go to reporting and filter by billable
    await goToReporting(page);

    await page.getByRole('button', { name: 'Billable' }).click();
    await page.getByRole('option', { name: 'Billable', exact: true }).click();

    await Promise.all([
        // escape
        page.keyboard.press('Escape'),
        // wait for API request to finish
        page.waitForResponse(response => response.url().includes('/time-entries/aggregate') && response.status() === 200)
    ]);
    await page.waitForLoadState('networkidle');

    await expect(page.getByText('1h 00min').first()).toBeVisible();
});


test('test that detailed view shows time entries correctly', async ({ page }) => {
    const projectName = 'Detailed View Project ' + Math.floor(Math.random() * 10000);

    // Create a time entry
    await createTimeEntryWithProject(page, projectName, '1h');

    // Go to detailed reporting view
    await goToReportingDetailed(page);

    // Verify the time entry is shown with all details
    await expect(page.getByText(projectName, { exact: true })).toBeVisible();
    await expect(page.locator('input[name="Duration"]')).toHaveValue('1h 00min');
    await expect(page.getByText('Time entry for ' + projectName, { exact: true })).toBeVisible();
});

test('test that updating duration in detailed view works correctly', async ({ page }) => {
    const projectName = 'Duration Update Project ' + Math.floor(Math.random() * 10000);
    const initialDuration = '1h';
    const updatedDuration = '2h 30min';

    // Create a time entry with initial duration
    await createTimeEntryWithProject(page, projectName, initialDuration);

    // Go to detailed reporting view
    await goToReportingDetailed(page);

    // Find and update the duration
    const durationInput = page.locator('input[name="Duration"]').first();
    await durationInput.click();
    await durationInput.fill(updatedDuration);
    await durationInput.press('Enter');

    // Wait for the update to be processed
    await page.waitForLoadState('networkidle');

    // Verify the new duration is displayed
    await expect(durationInput).toHaveValue(updatedDuration);
});

// TODO: test that date range filtering works in reporting
