import os

from hac_playwright.pages.base import BasePage
from playwright.sync_api import expect

CLOUD_FILESYSTEM_PATH = "/home/llmstudio/mount/data/user/oasst"
LOCAL_FILESYSTEM_PATH = os.path.join(os.getcwd(), "data/user/oasst")


class LLMStudioPage(BasePage):
    # Constants for selectors
    DATASET_IMPORT_SOURCE_SELECTOR = "dataset/import/source"
    CONTINUE_BUTTON_SELECTOR = "button[name='Continue']"
    DATASET_LIST_DELETE_SELECTOR = "dataset/list/delete"
    DATASET_DELETE_DIALOG_SELECTOR = "dataset/delete/dialog"
    DATASET_DELETE_SELECTOR = "dataset/delete"
    EXPERIMENT_RUN_SELECTOR = "experiment/start/run"
    EXPERIMENT_NAME_SELECTOR = "experiment/start/cfg/experiment_name"
    EXPERIMENT_METRIC_SELECTOR = "experiment/start/cfg/metric"
    EXPERIMENT_LIST_DELETE_SELECTOR = "experiment/list/delete"
    EXPERIMENT_DELETE_DIALOG_SELECTOR = "experiment/delete/dialog"
    EXPERIMENT_DELETE_SELECTOR = "experiment/delete"
    EXPERIMENT_STATUS_SELECTOR = "[data-automation-key='status']"
    EXPERIMENT_INDEX_SELECTOR = "[data-automation-key='name']"
    FILESYSTEM_SELECTOR = "dataset/import/local_path"
    FILENAME_SELECTOR = "dataset/import/name"
    S3_BUCKET_SELECTOR = "dataset/import/s3_bucket"
    S3_ACCESS_KEY_SELECTOR = "dataset/import/s3_access_key"
    S3_SECRET_KEY_SELECTOR = "dataset/import/s3_secret_key"
    S3_FILENAME_SELECTOR = "dataset/import/s3_filename"
    AZURE_CONN_STRING = "dataset/import/azure_conn_string"
    AZURE_CONTAINER = "dataset/import/azure_container"
    AZURE_FILENAME = "dataset/import/azure_filename"
    KAGGLE_COMMAND = "dataset/import/kaggle_command"
    KAGGLE_USERNAME = "dataset/import/kaggle_username"
    KAGGLE_SECRET_KEY = "dataset/import/kaggle_secret_key"
    DATA_SAMPLING = "experiment/start/cfg/data_sample"
    MAX_LENGTH = "experiment/start/cfg/max_length"
    MAX_LENGTH_INFERENCE = "experiment/start/cfg/max_length_inference"
    MIXED_PRECISION = "experiment/start/cfg/mixed_precision"
    EXPERIMENT_REFRESH_SELECTOR = "experiment/list/refresh"
    GPU_WARNING_SELECTOR = "experiment/start/error/proceed"

    def assert_dataset_import(self, dataset_name: str):
        dataset = self.page.get_by_role("button", name=dataset_name)
        # Assert that the element is not None and clickable
        assert dataset is not None
        dataset.click()

    def get_by_test_id(self, test_id):
        selector = f'[data-test="{test_id}"]'
        return self.page.locator(selector)

    def open_home_page(self):
        self.page.get_by_role("button", name="Home").click()

    def open_app_settings(self):
        self.page.get_by_role("button", name="Settings").click()

    def dataset_name(self, filename):
        self.get_by_test_id(self.FILENAME_SELECTOR).fill(filename)
        self.continue_button().click()
        self.continue_button().click()

    def import_dataset_from_filesystem(self, filepath: str):
        self.import_dataset("Local")
        if "LOCAL_LOGIN" in os.environ:
            path = f"{LOCAL_FILESYSTEM_PATH}/{filepath}"
        else:
            path = f"{CLOUD_FILESYSTEM_PATH}/{filepath}"
        self.get_by_test_id(self.FILESYSTEM_SELECTOR).fill(path)
        self.continue_button().click()

    def continue_button(self):
        return self.page.get_by_role("button", name="Continue")

    def import_dataset(self, source: str):
        button = self.page.get_by_role("button", name="Import dataset")
        button.click()
        # FIX: Selectors.set_test_id_attribute(self, "data-test")
        dropdown = self.get_by_test_id(self.DATASET_IMPORT_SOURCE_SELECTOR)
        dropdown.click()
        self.page.get_by_role("option", name=source).click()

    def import_dataset_from_aws(
        self, bucket: str, access_key: str, secret_key: str, dataset_name: str
    ):
        self.import_dataset("AWS S3")
        self.get_by_test_id(self.S3_BUCKET_SELECTOR).fill(bucket)
        self.get_by_test_id(self.S3_ACCESS_KEY_SELECTOR).fill(access_key)
        self.get_by_test_id(self.S3_SECRET_KEY_SELECTOR).fill(secret_key)
        self.get_by_test_id(self.S3_FILENAME_SELECTOR).fill(dataset_name)
        self.continue_button().click()

    def import_dataset_from_azure(
        self, connection: str, container: str, dataset_name: str
    ):
        self.import_dataset("Azure Blob Storage")
        self.get_by_test_id(self.AZURE_CONN_STRING).fill(connection)
        self.get_by_test_id(self.AZURE_CONTAINER).fill(container)
        self.get_by_test_id(self.AZURE_FILENAME).fill(dataset_name)
        self.continue_button().click()

    def import_dataset_from_kaggle(
        self, kaggle_command: str, username: str, secret: str
    ):
        self.import_dataset("Kaggle")
        self.get_by_test_id(self.KAGGLE_COMMAND).fill(kaggle_command)
        self.get_by_test_id(self.KAGGLE_USERNAME).fill(username)
        self.get_by_test_id(self.KAGGLE_SECRET_KEY).fill(secret)
        self.continue_button().click()

    def delete_dataset(self, dataset_name: str):
        # Go to dataset page
        self.view_datasets()
        self.get_by_test_id(self.DATASET_LIST_DELETE_SELECTOR).click()
        # Locate dataset to delete
        self.page.get_by_role("gridcell", name=dataset_name).click()
        # Confirm dataset deletion
        self.get_by_test_id(self.DATASET_DELETE_DIALOG_SELECTOR).click()
        # Delete dataset
        self.get_by_test_id(self.DATASET_DELETE_SELECTOR).click()

    def view_datasets(self):
        self.page.get_by_role("button", name="View datasets").click()

    def assert_dataset_deletion(self, dataset_name: str):
        self.view_datasets()
        dataset = self.page.get_by_role("button", name=dataset_name)
        # Assert that the element not found
        expect(dataset).not_to_be_visible()

    def create_experiment(self, name: str):
        self.page.get_by_role("button", name="Create experiment").click()
        self.experiment_name(name)

    def slider(self, slider_selector, target_value: str, step: float = 0.1):
        """
        Moves the slider to the target value.

        WARNING: step = 1.0 works only without headless mode.
        Possible bug in playwright!

        Args:
            slider_selector (str): The selector for the slider element.
            target_value (str): The target value for the slider.
            step (float, optional): The step size for moving the slider.
        """
        is_completed = False
        i = 0.0
        # Get the slider element
        slider = self.get_by_test_id(slider_selector)
        slider.click()
        # Get the bounding box of the slider
        bounding_box = slider.bounding_box()
        x1 = bounding_box["x"]
        y = bounding_box["y"] + bounding_box["height"] / 2

        while not is_completed:
            if i > 100:
                raise Exception("Could not find the target value")
            self.page.mouse.move(x1, y)
            self.page.mouse.down()
            x2 = bounding_box["x"] + bounding_box["width"] * float(i) / 100
            self.page.mouse.move(x2, y)
            self.page.mouse.up()
            value_now = slider.get_attribute("aria-valuenow")
            if float(value_now) == float(target_value):
                is_completed = True
            else:
                # Move the slider a little bit (adjust the step as needed)
                x1 = x2
                i += step

    def run_experiment(self):
        self.get_by_test_id(self.EXPERIMENT_RUN_SELECTOR).click()
        locator = self.get_by_test_id(self.GPU_WARNING_SELECTOR)
        if locator.is_visible():
            locator.click()

    def experiment_name(self, name: str):
        self.get_by_test_id(self.EXPERIMENT_NAME_SELECTOR).fill(name)

    def llm_backbone(self, value: str):
        self.page.get_by_role("combobox", name="LLM Backbone").fill(value)

    def mixed_precision(self, value: bool):
        old_toggle_value = self.get_by_test_id(self.MIXED_PRECISION).get_attribute(
            "aria-checked"
        )
        assert old_toggle_value in ["true", "false"]
        assert value in ["true", "false"]

        if old_toggle_value != value:
            self.get_by_test_id(self.MIXED_PRECISION).click()

    def data_sample(self, value):
        self.slider(self.DATA_SAMPLING, value)

    def max_length(self, value):
        self.slider(self.MAX_LENGTH, value)

    def max_length_inference(self, value):
        self.slider(self.MAX_LENGTH_INFERENCE, value)

    def metric(self, value):
        self.get_by_test_id(self.EXPERIMENT_METRIC_SELECTOR).click()
        self.page.get_by_role("option", name=f"{value}").click()

    def view_experiment_page(self):
        self.page.get_by_role("button", name="View experiments").click()

    def view_experiment(self, experiment_name: str):
        self.view_experiment_page()
        i = self.find_experiment_index(experiment_name)
        status = self.page.locator(
            f"{self.EXPERIMENT_STATUS_SELECTOR} >> nth={i}"
        ).inner_text()
        while True:
            if status in ["queued", "running"]:
                self.get_by_test_id(self.EXPERIMENT_REFRESH_SELECTOR).click()
                status = self.page.locator(
                    f"{self.EXPERIMENT_STATUS_SELECTOR} >> nth={i}"
                ).inner_text()
            elif status == "finished":
                break
            else:
                raise Exception(f"Unexpected status: {status}")

    def find_experiment_index(self, experiment_name):
        index = 0
        while index < 100:  # number of experiments
            # Get the innerText of the element with the specified selector
            inner_text = self.page.locator(
                f"{self.EXPERIMENT_INDEX_SELECTOR} >> nth={index}"
            ).inner_text()
            # Check if the current name matches the target name
            if inner_text != experiment_name:
                index += 1
            else:
                break
        return index

    def delete_experiment(self, experiment_name: str):
        # Go to experiment page
        self.view_experiment_page()
        # Click on Delete experiments button
        self.get_by_test_id(self.EXPERIMENT_LIST_DELETE_SELECTOR).click()
        # Locate experiment to delete
        self.page.get_by_role("gridcell", name=experiment_name).locator(
            f'div:has-text("{experiment_name}")'
        ).first.click()
        # Delete experiment
        self.get_by_test_id(self.EXPERIMENT_DELETE_DIALOG_SELECTOR).click()
        # Confirm experiment deletion
        self.get_by_test_id(self.EXPERIMENT_DELETE_SELECTOR).click()

    def assert_experiment_deletion(self, experiment_name: str):
        # Go to experiment page
        self.view_experiment_page()
        experiment = self.page.get_by_role("button", name=experiment_name)
        # Assert that the element not found
        expect(experiment).not_to_be_visible()