package ru.kovbasa.pages;

import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Mouse;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.PlaywrightException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ru.kovbasa.playwright.TestConfig;

public class CatalogPage extends BasePage {

    private static final Pattern DURATION_PATTERN = Pattern.compile("(\\d+)\\s+месяц");

    public CatalogPage(Page page, TestConfig config) {
        super(page, config);
    }

    public void open() {
        openPath("/catalog/courses");
    }

    public boolean isDefaultDirectionSelected() {
        return isOptionChecked("Все направления");
    }

    public boolean isDefaultLevelSelected() {
        return isOptionChecked("Любой уровень сложности")
                || isOptionChecked("Любой уровень");
    }

    public void setDurationRange(int minMonths, int maxMonths) {
        Locator durationSection = getFilterSection("Продолжительность");

        Locator sliders = page.locator("[role='slider']");
        if (sliders.count() >= 2) {
            Locator minSlider = sliders.nth(0);
            Locator maxSlider = sliders.nth(1);

            String minBefore = minSlider.getAttribute("aria-valuenow");
            String maxBefore = maxSlider.getAttribute("aria-valuenow");

            dragSliderTo(minSlider, minMonths);
            dragSliderTo(maxSlider, maxMonths);

            String minAfter = minSlider.getAttribute("aria-valuenow");
            String maxAfter = maxSlider.getAttribute("aria-valuenow");

            if (minBefore != null && minBefore.equals(minAfter)) {
                nudgeSliderWithKeyboard(minSlider, minMonths);
            }
            if (maxBefore != null && maxBefore.equals(maxAfter)) {
                nudgeSliderWithKeyboard(maxSlider, maxMonths);
            }
            waitForDurationFilterApplied(minMonths, maxMonths);
            return;
        }

        Locator inputs = durationSection.locator("input");
        if (inputs.count() >= 2) {
            String type = inputs.nth(0).getAttribute("type");
            if (type != null && (type.equals("number") || type.equals("text"))) {
                inputs.nth(0).fill(String.valueOf(minMonths));
                inputs.nth(1).fill(String.valueOf(maxMonths));
                inputs.nth(1).press("Enter");
                return;
            }
        }

        Locator rangeInputs = durationSection.locator("input[type='range']");
        if (rangeInputs.count() >= 2) {
            rangeInputs.nth(0).fill(String.valueOf(minMonths));
            rangeInputs.nth(1).fill(String.valueOf(maxMonths));
            return;
        }

        Locator rangeLabel = durationSection.locator("label").filter(
                new Locator.FilterOptions().setHasText(String.valueOf(minMonths))
        ).filter(new Locator.FilterOptions().setHasText(String.valueOf(maxMonths))).first();
        if (rangeLabel.count() > 0) {
            rangeLabel.click();
            return;
        }

        Locator rangeText = durationSection.locator(
                "text=/\\b" + minMonths + "\\b.*\\b" + maxMonths + "\\b/").first();
            if (rangeText.count() > 0) {
                rangeText.click();
                waitForDurationFilterApplied(minMonths, maxMonths);
                return;
            }
    }

    public void selectDirection(String direction) {
        Locator option = page.getByText(direction).first();
        try {
            option.click(new Locator.ClickOptions().setForce(true));
        } catch (PlaywrightException ex) {
            option.evaluate("el => el.click()");
        }
    }

    public boolean isDirectionSelected(String direction) {
        return isOptionChecked(direction);
    }

    public String getFirstCourseTitle() {
        Locator card = getCourseCards().first();
        card.waitFor();
        Locator title = card.locator("h4, h5, h6").first();
        String text = title.textContent();
        return text == null ? "" : text.trim();
    }

    public List<String> getCourseTitles(int limit) {
        Locator cards = getCourseCards();
        Locator titles = cards.locator("h4, h5, h6");
        titles.first().waitFor();
        List<String> all = titles.allTextContents();
        return all.size() > limit ? all.subList(0, limit) : all;
    }

    public void resetFilters() {
        Locator reset = page.locator("button:has-text('Сбросить фильтр'),"
                + " button:has-text('Сбросить фильтры'),"
                + " button:has-text('Очистить фильтры'),"
                + " a:has-text('Очистить фильтры'),"
                + " a:has-text('Сбросить фильтры')").first();
        reset.click();
        Locator allDirections = page.getByText("Все направления").first();
        if (allDirections.count() > 0) {
            allDirections.click(new Locator.ClickOptions().setForce(true));
        }
        page.waitForFunction("() => {\n"
                + "  const label = Array.from(document.querySelectorAll('label'))\n"
                + "    .find(el => (el.textContent || '').trim() === 'Все направления');\n"
                + "  if (!label) return false;\n"
                + "  const id = label.getAttribute('for');\n"
                + "  const input = id ? document.getElementById(id) : label.querySelector('input');\n"
                + "  const defaultSelected = input ? input.checked : true;\n"
                + "  const duration = document.body.innerText.includes('От 0 до 15 месяцев');\n"
                + "  return defaultSelected && duration;\n"
                + "}");
    }


    public List<Integer> getVisibleCourseDurations() {
        List<Integer> values = new ArrayList<>();
        Locator cards = getCourseCards();
        int count = (int) Math.min(cards.count(), 30);
        for (int i = 0; i < count; i++) {
            String text = cards.nth(i).innerText();
            Matcher matcher = DURATION_PATTERN.matcher(text);
            if (matcher.find()) {
                values.add(Integer.parseInt(matcher.group(1)));
            } else {
                values.add(-1);
            }
        }
        return values;
    }

    private Locator getFilterSection(String filterLabel) {
        Locator title = page.getByText(filterLabel).first();
        Locator section = title.locator("xpath=ancestor::*[self::div or self::section][1]");
        if (section.count() == 0) {
            section = title.locator("xpath=ancestor::*[self::div or self::section][2]");
        }
        return section;
    }

    private boolean isOptionChecked(String optionText) {
        Boolean checked = (Boolean) page.evaluate("text => {\n"
                + "  const labels = Array.from(document.querySelectorAll('label'))\n"
                + "    .filter(el => (el.textContent || '').trim() === text);\n"
                + "  if (labels.length) {\n"
                + "    const label = labels[0];\n"
                + "    const id = label.getAttribute('for');\n"
                + "    if (id) {\n"
                + "      const input = document.getElementById(id);\n"
                + "      if (input) return !!input.checked;\n"
                + "    }\n"
                + "    const nested = label.querySelector('input');\n"
                + "    if (nested) return !!nested.checked;\n"
                + "  }\n"
                + "  const el = Array.from(document.querySelectorAll('*'))\n"
                + "    .find(node => (node.textContent || '').trim() === text);\n"
                + "  if (!el) return null;\n"
                + "  const aria = el.getAttribute('aria-checked') || el.getAttribute('aria-pressed');\n"
                + "  if (aria !== null) return aria === 'true';\n"
                + "  const input = el.querySelector('input') || el.closest('div,li,section')?.querySelector('input');\n"
                + "  if (input) return !!input.checked;\n"
                + "  return null;\n"
                + "}", optionText);
        return Boolean.TRUE.equals(checked);
    }

    private Locator getCourseCards() {
        Locator list = page.locator("section, div").filter(
                new Locator.FilterOptions().setHasText("Показать еще")
        ).first();
        Locator cards = list.locator("a[href*='/lessons/']:visible");
        if (cards.count() == 0) {
            cards = list.locator("a:has(h4):visible, a:has(h5):visible, a:has(h6):visible");
        }
        if (cards.count() == 0) {
            cards = page.locator("main a[href*='/lessons/']:visible");
        }
        return cards;
    }

    private void dragSliderTo(Locator slider, int targetValue) {
        String minText = slider.getAttribute("aria-valuemin");
        String maxText = slider.getAttribute("aria-valuemax");
        int min = minText == null || minText.isBlank() ? 0 : Integer.parseInt(minText);
        int max = maxText == null || maxText.isBlank() ? 15 : Integer.parseInt(maxText);
        if (targetValue < min) {
            targetValue = min;
        }
        if (targetValue > max) {
            targetValue = max;
        }
        @SuppressWarnings("unchecked")
        Map<String, Object> trackBox = (Map<String, Object>) slider.evaluate("el => {"
                + "const track = el.parentElement;"
                + "const r = track.getBoundingClientRect();"
                + "return {x: r.x, y: r.y, width: r.width, height: r.height};"
                + "}");
        if (trackBox == null) {
            return;
        }
        double x = ((Number) trackBox.get("x")).doubleValue();
        double width = ((Number) trackBox.get("width")).doubleValue();
        double y = ((Number) trackBox.get("y")).doubleValue();
        double height = ((Number) trackBox.get("height")).doubleValue();
        double ratio = (targetValue - min) / (double) (max - min);
        double targetX = x + width * ratio;
        double targetY = y + height / 2;

        var handleBox = slider.boundingBox();
        if (handleBox == null) {
            return;
        }
        page.mouse().move(handleBox.x + handleBox.width / 2, handleBox.y + handleBox.height / 2);
        page.mouse().down();
        page.mouse().move(targetX, targetY, new Mouse.MoveOptions().setSteps(10));
        page.mouse().up();
    }

    private void nudgeSliderWithKeyboard(Locator slider, int targetValue) {
        String minText = slider.getAttribute("aria-valuemin");
        String maxText = slider.getAttribute("aria-valuemax");
        String currentText = slider.getAttribute("aria-valuenow");
        int min = minText == null || minText.isBlank() ? 0 : Integer.parseInt(minText);
        int max = maxText == null || maxText.isBlank() ? 15 : Integer.parseInt(maxText);
        int current = currentText == null || currentText.isBlank() ? min : Integer.parseInt(currentText);
        if (targetValue < min) {
            targetValue = min;
        }
        if (targetValue > max) {
            targetValue = max;
        }
        slider.focus();
        int steps = Math.abs(targetValue - current);
        String key = targetValue > current ? "ArrowRight" : "ArrowLeft";
        for (int i = 0; i < steps; i++) {
            page.keyboard().press(key);
        }
    }

    private void waitForDurationFilterApplied(int minMonths, int maxMonths) {
        page.waitForFunction(
                "([min, max]) => {\n"
                        + "  const cards = Array.from(document.querySelectorAll(\"a[href*='/lessons/']\"));\n"
                        + "  const visible = cards.filter(el => !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length));\n"
                        + "  const durations = visible.map(el => {\n"
                        + "    const m = el.innerText.match(/(\\d+)\\s+месяц/);\n"
                        + "    return m ? parseInt(m[1], 10) : null;\n"
                        + "  }).filter(v => v !== null);\n"
                        + "  if (!durations.length) return false;\n"
                        + "  return durations.every(v => v >= min && v <= max);\n"
                        + "}",
                new Object[] {minMonths, maxMonths});
    }
}
