package de.avatic.lcc.e2e.tests;

import com.microsoft.playwright.Locator;
import com.microsoft.playwright.options.WaitForSelectorState;
import de.avatic.lcc.e2e.pages.AssistantPage;
import de.avatic.lcc.e2e.pages.CalculationEditPage;
import de.avatic.lcc.e2e.pages.ResultsPage;
import de.avatic.lcc.e2e.testdata.DestinationInput;
import de.avatic.lcc.e2e.testdata.TestCase;
import de.avatic.lcc.e2e.testdata.TestCases;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.logging.Logger;
import java.util.stream.Stream;

/**
 * End-to-end tests for the calculation workflow.
 * Tests all scenarios from Testfälle.xlsx using Playwright.
 *
 * <p>The backend with integrated frontend is started automatically via @SpringBootTest.
 *
 * <p>Run with: {@code mvn test -Dtest=CalculationWorkflowE2ETest -Dgroups=e2e -Dspring.profiles.active=test,dev,mysql}
 */
@DisplayName("Calculation Workflow E2E Tests")
class CalculationWorkflowE2ETest extends AbstractE2ETest {

    private static final Logger logger = Logger.getLogger(CalculationWorkflowE2ETest.class.getName());

    // Maximum time to wait for calculation to complete (in milliseconds)
    private static final int CALCULATION_TIMEOUT_MS = 120000; // 2 minutes
    private static final int POLL_INTERVAL_MS = 2000; // Check every 2 seconds

    @ParameterizedTest(name = "Testfall {0}: {1}")
    @MethodSource("provideTestCases")
    @DisplayName("Calculation workflow")
    void testCalculationWorkflow(String id, String name, TestCase testCase) {
        logger.info(() -> "Starting test case: " + id + " - " + name);

        try {
            // 1. Navigate to assistant and search part numbers
            AssistantPage assistant = new AssistantPage(page);
            assistant.navigate(getBaseUrl());
            assistant.searchPartNumbers(testCase.input().partNumber());

            // 2. Select supplier
            assistant.deletePreselectedSuppliers();
            assistant.selectSupplier(testCase.input().supplierName());

            // 3. Create calculation
            assistant.createCalculation(testCase.input().loadFromPrevious());

            // 4. Fill the calculation form
            CalculationEditPage calcPage = new CalculationEditPage(page);
            calcPage.fillForm(testCase.input());

            // 5. Add and fill destinations
            for (DestinationInput dest : testCase.input().destinations()) {
                calcPage.addDestination(dest);
                calcPage.fillDestination(dest);
            }

            // 6. Take screenshot before clicking Calculate & close
            takeScreenshot("before_calculate_" + id);

            // 7. Click "Calculate & close" button
            Locator calcButton = page.locator("xpath=//button[contains(., 'Calculate & close')]");
            calcButton.waitFor();

            if (!calcButton.isEnabled()) {
                throw new AssertionError("Calculate & close button is not enabled");
            }

            logger.info(() -> "Clicking Calculate & close for test case " + id);
            calcButton.click();

            // 8. Wait for navigation to calculations list
            page.waitForURL("**/calculations**", new com.microsoft.playwright.Page.WaitForURLOptions().setTimeout(10000));
            logger.info(() -> "Navigated to calculations page");

            // 9. Wait for calculation to complete
            boolean completed = waitForCalculationComplete(testCase.input().partNumber());
            if (!completed) {
                takeScreenshot("calculation_timeout_" + id);
                throw new AssertionError("Calculation did not complete within timeout");
            }

            takeScreenshot("calculation_completed_" + id);
            logger.info(() -> "Test case " + id + " - calculation completed!");

            // 10. Navigate to Reports and verify results
            ResultsPage resultsPage = new ResultsPage(page);
            resultsPage.navigateToReports(getBaseUrl(), testCase.input().partNumber(), testCase.input().supplierName());

            takeScreenshot("report_" + id);

            // 11. Verify results match expected values
            resultsPage.verifyResults(testCase.expected(), TOLERANCE);

            logger.info(() -> "Test case " + id + " - all results verified successfully!");

        } catch (Exception e) {
            // Take screenshot on failure
            takeScreenshot("failure_" + id);
            logger.severe(() -> "Test case " + id + " failed: " + e.getMessage());
            throw e;
        }
    }

    /**
     * Waits for a calculation to complete by polling the calculations list.
     * Looks for a COMPLETED badge for the given part number.
     *
     * @param partNumber the part number to look for
     * @return true if calculation completed, false if timeout
     */
    private boolean waitForCalculationComplete(String partNumber) {
        logger.info("Waiting for calculation to complete for: " + partNumber);

        long startTime = System.currentTimeMillis();
        int attempts = 0;

        while (System.currentTimeMillis() - startTime < CALCULATION_TIMEOUT_MS) {
            attempts++;
            final int attemptNum = attempts;

            // Wait a bit for dashboard to update (it pulls every few seconds)
            page.waitForTimeout(POLL_INTERVAL_MS);

            // Check the "Running" counter in the dashboard
            // Structure: .dashboard-box contains .dashboard-box-number (the count) and .dashboard-box-number-text (the label)
            Locator runningBox = page.locator(".dashboard-box:has(.dashboard-box-number-text:text-is('Running'))");

            if (runningBox.count() > 0) {
                Locator runningCount = runningBox.locator(".dashboard-box-number");
                if (runningCount.count() > 0) {
                    String runningText = runningCount.textContent().trim();
                    logger.info("Attempt " + attemptNum + ": Running calculations = " + runningText);

                    try {
                        int running = Integer.parseInt(runningText);
                        if (running == 0) {
                            // No more running calculations - check if ours completed or failed
                            logger.info("No running calculations. Checking final status...");

                            // Check the Failed counter
                            Locator failedBox = page.locator(".dashboard-box:has(.dashboard-box-number-text:text-is('Failed'))");
                            if (failedBox.count() > 0) {
                                Locator failedCount = failedBox.locator(".dashboard-box-number");
                                String failedText = failedCount.textContent().trim();
                                int failed = Integer.parseInt(failedText);
                                if (failed > 0) {
                                    logger.severe("Calculation failed! Failed count: " + failed);
                                    takeScreenshot("calculation_failed");
                                    return false;
                                }
                            }

                            // Check the Completed counter increased
                            Locator completedBox = page.locator(".dashboard-box:has(.dashboard-box-number-text:text-is('Completed'))");
                            if (completedBox.count() > 0) {
                                Locator completedCount = completedBox.locator(".dashboard-box-number");
                                String completedText = completedCount.textContent().trim();
                                logger.info("Completed calculations: " + completedText);
                            }

                            logger.info("Calculation completed after " + attemptNum + " attempts");
                            return true;
                        }
                    } catch (NumberFormatException e) {
                        logger.warning("Could not parse running count: " + runningText);
                    }
                }
            } else {
                // Dashboard not found, try refreshing
                logger.info("Attempt " + attemptNum + ": Dashboard not found, refreshing...");
                page.reload();
                page.waitForLoadState();
            }
        }

        logger.warning("Calculation did not complete within " + CALCULATION_TIMEOUT_MS + "ms");
        takeScreenshot("calculation_timeout");
        return false;
    }

    static Stream<Arguments> provideTestCases() {
        return TestCases.ALL.stream()
            .map(tc -> Arguments.of(tc.id(), tc.name(), tc));
    }
}
