package de.avatic.lcc.e2e.tests;

import com.microsoft.playwright.Locator;
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 de.avatic.lcc.e2e.util.DeviationReport;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.logging.Logger;

/**
 * Runs all test cases and generates a deviation report comparing expected vs actual values.
 * This test does not fail on deviations - it collects them all and prints a summary.
 */
@DisplayName("Deviation Analysis E2E Test")
@Tag("analysis")
class DeviationAnalysisE2ETest extends AbstractE2ETest {

    private static final Logger logger = Logger.getLogger(DeviationAnalysisE2ETest.class.getName());
    private static final int CALCULATION_TIMEOUT_MS = 120000;
    private static final int POLL_INTERVAL_MS = 2000;

    @Test
    @DisplayName("Analyze deviations across all test cases")
    void analyzeDeviations() {
        DeviationReport report = new DeviationReport();

        for (TestCase testCase : TestCases.ALL) {
            String id = testCase.id();
            String name = testCase.name();

            logger.info(() -> "\n========================================");
            logger.info(() -> "Processing test case: " + id + " - " + name);
            logger.info(() -> "========================================\n");

            try {
                // Run the calculation workflow
                Map<String, Object> actualResults = runCalculationAndGetResults(testCase);

                // Add to deviation report
                report.addTestCase(id, name, testCase.expected(), actualResults);

                logger.info(() -> "Test case " + id + " completed successfully");

            } catch (Exception e) {
                logger.severe(() -> "Test case " + id + " failed with error: " + e.getMessage());
                report.addError(id, name, e.getMessage());
                takeScreenshot("error_" + id);
            }
        }

        // Print the deviation report
        String reportContent = report.generateMarkdownTable();
        System.out.println(reportContent);
        logger.info(reportContent);

        // Write report to file
        try {
            Path reportPath = Path.of("target/deviation-report.md");
            Files.writeString(reportPath, reportContent);
            logger.info("Deviation report written to: " + reportPath.toAbsolutePath());
        } catch (IOException e) {
            logger.warning("Could not write deviation report file: " + e.getMessage());
        }
    }

    private Map<String, Object> runCalculationAndGetResults(TestCase testCase) {
        // 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);

        // Enable screenshots for debugging
        calcPage.enableScreenshots("case_" + testCase.id());

        calcPage.fillForm(testCase.input());

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

        // 6. Take screenshot before clicking Calculate
        calcPage.screenshotBeforeCalculate();

        // 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 for test case " + testCase.id());
        }

        calcButton.click();

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

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

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

        // 11. Take full page screenshot with all collapsible boxes expanded
        resultsPage.takeFullPageScreenshot("report_" + testCase.id());

        // 12. Read and return results (without verification)
        return resultsPage.readResults();
    }

    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;

            page.waitForTimeout(POLL_INTERVAL_MS);

            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();

                    try {
                        int running = Integer.parseInt(runningText);
                        if (running == 0) {
                            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);
                                    return false;
                                }
                            }
                            return true;
                        }
                    } catch (NumberFormatException e) {
                        logger.warning("Could not parse running count: " + runningText);
                    }
                }
            } else {
                page.reload();
                page.waitForLoadState();
            }
        }

        return false;
    }
}
