# -*- coding:utf-8 -*-
import os
import random
import re
import time

import allure
import pytest
from playwright.sync_api import expect, Page
from BuildInLibrary.BuildInLibrary import BuildInLibrary
from Common.AllurePretty import PrettyAllure
from Config import Config
from Common.ImageComparison import ImageComparisonTool
from Utils.Debug_talk import DubugTalk
from Utils.logger_util import write_log, error_log


class BasePage:

    def __init__(self, page: Page):
        self.page = page
        self.project_id = DubugTalk().read_extract_file("project_id")
        # 工程管理url
        self.project_management_url = Config.url
        # 变量管理url
        self.variable_management_url = Config.url + f"/#/editing/project/{self.project_id}/vars"
        # 历史管理url
        self.history_management_url = Config.url + f"/#/editing/project/{self.project_id}/history"
        # 基础管理url
        self.basis_management_url = Config.url + f"/#/editing/project/{self.project_id}/basis"
        # 数据管理url
        self.data_management_url = Config.url + f"/#/editing/project/{self.project_id}/data"
        # 画面管理url
        # self.page_management_url ="http://127.0.0.1:18000/#/editing/project/66/page"
        self.page_management_url = Config.url + f"/#/editing/project/{self.project_id}/page"
        # 报警管理url
        self.alarm_management_url = Config.url + f"/#/editing/project/{self.project_id}/alarm"
        # 多语言管理url
        self.language_management_url = Config.url + f"/#/editing/project/{self.project_id}/lang"
        # 外设管理url
        self.device_management_url = Config.url + f"/#/editing/project/{self.project_id}/externalDevice"
        # 互联管理url
        self.connection_management_url = Config.url + f"/#/editing/project/{self.project_id}/connection"
        # 自动化管理url
        self.automation_management_url = Config.url + f"/#/editing/project/{self.project_id}/automation"


    def _goto_url(self, value):
        """打开网页"""
        if value == "工程":
            self.page.goto(self.project_management_url)
        elif value == "画面":
            self.page.goto(self.page_management_url)
        elif value == "变量":
            self.page.goto(self.variable_management_url)
        elif value == "历史":
            self.page.goto(self.history_management_url)
        elif value == "基础":
            self.page.goto(self.basis_management_url)
        elif value == "数据":
            self.page.goto(self.data_management_url)
        elif value == "报警":
            self.page.goto(self.alarm_management_url)
        elif value == "多语言":
            self.page.goto(self.language_management_url)
        elif value == "外设":
            self.page.goto(self.device_management_url)
        elif value == "互联":
            self.page.goto(self.connection_management_url)
        elif value == "自动化":
            self.page.goto(self.automation_management_url)

    # # 进入工程管理页面
    # def _goto_url_project_management(self):
    #     project_management_url = Config.url
    #     self._goto_url(project_management_url)
    #
    # # 进入变量管理页面
    # def _goto_url_variable_management(self):
    #     # 拼接变量管理访问地址
    #     variable_management_url = Config.url + f"/#/editing/project/{self.project_id}/vars"
    #     self._goto_url(variable_management_url)
    #
    # # 进入历史库页面
    # def _goto_url_history_management(self):
    #     # 拼接历史库访问地址
    #     history_management_url = Config.url + f"/#/editing/project/{self.project_id}/history"
    #     self._goto_url(history_management_url)
    #
    # # 进入页面管理页面
    # def _goto_url_page_management(self):
    #     # page_management_url = "http://127.0.0.1:18000/#/editing/project/127/page"
    #     page_management_url = Config.url + f"/#/editing/project/{self.project_id}/page"
    #     self._goto_url(page_management_url)
    #
    # # 进入基础页面
    # def _goto_url_basis_management(self):
    #     # 拼接基础页面访问地址
    #     basis_management_url = Config.url + f"/#/editing/project/{self.project_id}/basis"
    #     self._goto_url(basis_management_url)
    #
    # # 进入数据管理页面
    # def _goto_url_data_management(self):
    #     # 拼接数据管理访问地址
    #     data_management_url = Config.url + f"/#/editing/project/{self.project_id}/data"
    #     self._goto_url(data_management_url)
    #
    # # 进入报警管理页面
    # def _goto_url_alarm_management(self):
    #     # 拼接报警管理访问地址
    #     alarm_management_url = Config.url + f"/#/editing/project/{self.project_id}/alarm"
    #     self._goto_url(alarm_management_url)
    #
    # # 进入多语言管理页面
    # def _goto_url_language_management(self):
    #     # 拼接多语言管理访问地址
    #     language_management_url = Config.url + f"/#/editing/project/{self.project_id}/lang"
    #     self._goto_url(language_management_url)
    #
    # # 进入外设管理页面
    # def _goto_url_device_management(self):
    #     # 拼接外设管理访问地址
    #     device_management_url = Config.url + f"/#/editing/project/{self.project_id}/externalDevice"
    #     self._goto_url(device_management_url)
    #
    # # 进入互联管理页面
    # def _goto_url_connection_management(self):
    #     # 拼接互联管理访问地址
    #     connection_management_url = Config.url + f"/#/editing/project/{self.project_id}/connection"
    #     self._goto_url(connection_management_url)
    #
    # # 进入自动化管理页面
    # def _goto_url_automation_management(self):
    #     # 拼接自动化管理访问地址
    #     automation_management_url = Config.url + f"/#/editing/project/{self.project_id}/automation"
    #     self._goto_url(automation_management_url)

    def _click(self, comm_locator=None, frame_locator=None, pw_locator=None):
        """
        点击元素
        :param comm_locator: 传入普通元素定位器
        :param frame_locator: 传入frame框架的的定位器
        :param pw_locator: 传入playwright自带的定位器
        :return:
        """
        try:
            if comm_locator is not None:
                self.page.click(comm_locator)
            if frame_locator is not None:
                if comm_locator is not None:
                    self.page.frame_locator(frame_locator).locator(comm_locator).click()
            if pw_locator is not None:
                return pw_locator.click()
        except Exception as e:
            error_log(e)


    def _hover(self, comm_locator=None, frame_locator=None, pw_locator=None):
        """
        点击元素
        :param comm_locator: 传入普通元素定位器
        :param frame_locator: 传入frame框架的的定位器
        :param pw_locator: 传入playwright自带的定位器
        :return:
        """
        try:
            if frame_locator is not None:
                if comm_locator is not None:
                    self.page.frame_locator(frame_locator).locator(comm_locator).hover()
            if comm_locator is not None:
                self.page.hover(comm_locator)
            if pw_locator is not None:
                pw_locator.hover()
        except Exception as e:
            error_log(e)


    def _fill(self, value, comm_locator=None, frame_locator=None, pw_locator=None):
        """
        定位元素，输入内容
        :param comm_locator:传入普通元素定位器
        :param value:传入输入的值
        :param frame_locator: 传入frame框架
        :param pw_locator: 传入playwright自带的定位器
        :return:
        """
        if not isinstance(value, str):
            value = str(value)
        else:
            value = value

        try:
            if frame_locator is not None:
                if comm_locator is not None:
                    self.page.frame_locator(selector=frame_locator).locator(selector_or_locator=comm_locator).fill(
                        value)
            if comm_locator is not None:
                self.page.fill(selector=comm_locator, value=value)
            if pw_locator is not None:
                pw_locator.fill(value)
        except Exception as e:
            error_log(e)
    def _press(self, comm_locator=None, frame_locator=None, pw_locator=None):
        """
        定位元素，回车键
        :param comm_locator:传入普通元素定位器
        :param value:传入输入的值
        :param frame_locator: 传入frame框架
        :param pw_locator: 传入playwright自带的定位器
        :return:
        """
        try:
            if frame_locator is not None:
                if comm_locator is not None:
                    self.page.frame_locator(selector=frame_locator).locator(selector_or_locator=comm_locator).press("Enter")
                if comm_locator is not None:
                    self.page.press("Enter")
                if pw_locator is not None:
                    pw_locator.press("Enter")
        except Exception as e:
            error_log(e)


    def _type(self, value, comm_locator=None, pw_locator=None, frame_locator=None):
        """
        模拟人工输入，一个键一个键的输入
        :param comm_locator:传入普通元素定位器
        :param value:传入输入的值
        :param frame_locator: 传入frame框架
        :param pw_locator: 传入playwright自带的定位器
        :return:
        """
        value = BuildInLibrary().repalce_parameter(value)
        try:
            if frame_locator is not None:
                if comm_locator is not None:
                    self.page.frame_locator(selector=frame_locator).locator(selector_or_locator=comm_locator).type(
                        text=value, delay=100)
            if comm_locator is not None:
                self.page.type(selector=comm_locator, text=value, delay=100)
            if pw_locator is not None:
                # 先清空原有的值
                pw_locator.fill("")
                # 输入新的值
                pw_locator.type(text=value, delay=100)
        except Exception as e:
            error_log(e)

    def _scroll_into_view_if_needed(self, comm_locator=None, frame_locator=None, pw_locator=None):
        """
        滚动到指定元素位置
        :param comm_locator: 传入普通元素定位器
        :param frame_locator: 传入frame框架的的定位器
        :param pw_locator: 传入playwright自带的定位器
        :return:
        """
        try:
            if frame_locator is not None:
                if comm_locator is not None:
                    self.page.frame_locator(frame_locator).locator(comm_locator).scroll_into_view_if_needed()
            if pw_locator is not None:
                return pw_locator.scroll_into_view_if_needed()
        except Exception as e:
            error_log(e)


    def _file(self, files, comm_locator=None, pw_locator=None, frame_locator=None):
        """
        上传文件的方法
        :param comm_locator: 普通定位器
        :param files: 单个文件名，或者列表存放多个文件
        :param frame_locator: iframe框架定位器，如果没有就不传
        :param pw_locator: playwright自带的定位器
        :return:
        """
        try:
            if frame_locator is not None:
                if comm_locator is not None:
                    self.page.frame_locator(frame_locator).locator(comm_locator).set_input_files(files=files)
            if comm_locator is not None:
                self.page.locator(comm_locator).set_input_files(files=files)
            if pw_locator is not None:
                pw_locator.set_input_files(files=files)
        except Exception as e:
            error_log(e)

    def _ele_to_be_visible(self, locator):
        """断言页面元素可见"""
        return expect(self.page.locator(locator)).to_be_visible()

    def _ele_to_be_visible_force(self, locator, frame_locator=None, timout: int = 5):
        """强制等待某个元素可见"""
        ele = None
        if frame_locator is not None:
            ele = self.page.frame_locator(frame_locator).locator(locator)
        else:
            ele = self.page.locator(locator)
        for t in range(0, timout):
            self.page.wait_for_timeout(500)
            if ele.is_visible():
                break
        else:
            raise Exception("元素未找到!")

    def _ele_is_checked(self, selector):
        """判断元素是否被选中"""
        return self.page.is_checked(selector)

    def _ele_to_contain_text(self, selector, substring):
        """判断元素是否包含指定的子文本"""
        return expect(self.page.locator(selector)).to_contain_text(substring)

    def _ele_to_have_text(self, selector, text):
        """判断元素是否包含指定的文本"""
        return expect(self.page.locator(selector)).to_have_text(re.compile(rf"{text}"))

    def _browser_operation(self, reload=False, forward=False, back=False, close=False):
        """浏览器操作，reload 刷新，forward 前进，back 后退"""
        if reload:
            self.page.reload()
        if back:
            self.page.go_back()
        if forward:
            self.page.go_forward()
        if close:
            self.page.close()

    def screenshot(self, path, full_page=True, locator=None):
        """截图功能，默认截取全屏，如果传入定位器表示截取元素"""
        if locator is not None:
            self.page.locator(locator).screenshot(path=path)
            return path
        self.page.screenshot(path=path, full_page=full_page)
        return path

    # def _del_auth(self):
    #     """删除授权文件"""
    #     auth_path = Config.auth_dir + os.path.sep + "auth.json"
    #     if os.path.exists(auth_path):
    #         os.remove(auth_path)

    def _get_one_element_from_list_by_random(self, parent_locator):
        """从列表中随机获取一个元素"""
        # 获取包含列表元素的父元素
        list_parent = self.page.query_selector(parent_locator)
        # 获取所有的列表元素
        list_items = list_parent.query_selector_all("li")
        # 从列表元素中随机选择一个元素
        random_index = random.randint(0, len(list_items) - 1)
        return list_items[random_index]

    def _drag_and_drop_to_user_position(self, source_selector, target_point_x, target_point_y):
        """
        拖拽元素到用户指定的位置，target_point_x和target_point_y为页面目标点的横纵坐标，
        需要用户自己计算（可使用一些屏幕坐标点测试工具进行测量，如:snipaste）
        source_selector: 拖拽元素的定位器
        target_point_x: 目标点的横坐标
        target_point_y: 目标点的纵坐标
        """
        if source_selector is not None:
            # 获取元素的边界信息，用于确定拖拽的起始点
            source_box = source_selector.bounding_box()
            if source_box is None:
                raise Exception("无法获取元素边界信息")
            # 计算拖拽的起始点（中心点）
            source_point = {
                'x': source_box['x'] + source_box['width'] / 2,
                'y': source_box['y'] + source_box['height'] / 2
            }
            # 执行拖拽操作到用户指定的位置
            self.page.mouse.move(source_point['x'], source_point['y'])
            self.page.mouse.down()
            # 移动鼠标到目标位置
            self.page.mouse.move(target_point_x, target_point_y)
            # 释放鼠标按钮
            self.page.mouse.up()

    def compare_images(self,filename,actual_picture,expect_picture,screenshot_area="editor"):
        actual_img = Config.img_dir + os.path.sep + filename + os.path.sep + "actual_img" + os.path.sep + f"{actual_picture}.png"  # actual_img_dir为存储实际截图的路径
        expect_img = Config.img_dir + os.path.sep + filename + os.path.sep + "expected_img" + os.path.sep + f"{expect_picture}.png"  # expected_img_dir为存储预期截图的路径
        crate_expect_img_path = Config.img_dir + os.path.sep + filename + os.path.sep + "expected_img"
        # compare_result_img = Config.result_img_dir + os.path.sep + f"{result_picture}.png"  # result_img_dir为存储对比结果截图的路
        # 运行版截图
        try:
            os.makedirs(crate_expect_img_path)
            print(f"Directory '{crate_expect_img_path}' created successfully")
        except FileExistsError:
            print(f"Directory '{crate_expect_img_path}' already exists")
        if screenshot_area == "vision":
            x = Config.vision_x_coordinate
            y = Config.vision_y_coordinate
            w = Config.vision_width
            h = Config.vision_height
        # 开发版截图
        elif screenshot_area == "editor":
            x = Config.editor_x_coordinate
            y = Config.editor_y_coordinate
            w = Config.editor_width
            h = Config.editor_height
        # 自定义参数截图
        else:
            x = Config.custom_x_coordinate
            y = Config.custom_x_coordinate
            w = Config.custom_x_coordinate
            h = Config.custom_x_coordinate
        #指定区域截图
        ScreenshotArea = {'x': x, 'y': y, 'width': w, 'height': h}
        self.page.screenshot(path=actual_img, full_page=True, clip=ScreenshotArea)
        comparator = ImageComparisonTool()
        result = comparator.compare_images(actual_img, expect_img)
        # 返回比对结果，true为相似，false为不相似
        write_log("进行图片比对...")
        write_log(f"实际图片：{actual_picture}.png,  预期图片：{expect_picture}.png", )
        write_log(f"比对结果：{result}",)
        return result

    def allure_reports(self,filename,actual_img_name,expect_img_name):
        actual_img = Config.img_dir + os.path.sep + filename + os.path.sep + "actual_img" + os.path.sep + f"{actual_img_name}.png"  # actual_img_dir为存储实际截图的路径
        expect_img = Config.img_dir + os.path.sep + filename + os.path.sep + "expected_img" + os.path.sep + f"{expect_img_name}.png"  # expected_img_dir为存储预期截图的路径
        with allure.step('实际截图'):
            allure.attach.file(actual_img, f"{actual_img_name}", allure.attachment_type.PNG)

        with allure.step('预期截图'):
            allure.attach.file(expect_img, f"{expect_img_name}", allure.attachment_type.PNG)


