from __future__ import annotations

from playwright.sync_api import Frame, Page, expect

from e2e.pages.wanzhanggui_order_page import WANYOUJI_CONSOLE_URL, WanyoujiFulfillmentPage
from e2e.utils.bug_reporter import BugReporter


PAYCENTER_READY_TEXTS = ["已完善", "已启用", "启用", "正常", "审核通过", "可用"]
PAYCENTER_NOT_READY_TEXTS = ["未完善", "未配置", "待完善", "停用", "禁用", "审核不通过"]


class PayCenterPage(WanyoujiFulfillmentPage):
    def __init__(self, page: Page, tenant_home_url: str, bug_reporter: BugReporter | None = None):
        super().__init__(page, tenant_home_url, bug_reporter)

    def ensure_merchant_payment_config_ready(
        self,
        *,
        merchant_keyword: str,
        merchant_no: str,
        account_subject: str,
        bank_name: str,
        bank_account: str,
        contact_name: str,
        contact_phone: str,
        pay_channel: str = "线下转账",
        id_card: str = "110101199001011234",
    ) -> str:
        frame = self.open_merchant_payment_config()
        self.search_merchant_if_possible(frame, merchant_keyword)
        text = frame.locator("body").inner_text(timeout=5000)
        if self.merchant_payment_config_ready(text, merchant_keyword):
            return text

        assert self.open_merchant_payment_config_editor(frame), (
            f"支付中心未找到商户支付信息新增/编辑入口；merchant={merchant_keyword}; text={text[:1500]}"
        )
        self.fill_merchant_payment_config_form(
            frame,
            {
                "商户号": merchant_no,
                "商户ID": merchant_no,
                "商户编号": merchant_no,
                "开户主体": account_subject,
                "账户主体": account_subject,
                "收款主体": account_subject,
                "开户名": account_subject,
                "户名": account_subject,
                "开户行": bank_name,
                "银行": bank_name,
                "银行卡号": bank_account,
                "银行账号": bank_account,
                "对公账户": bank_account,
                "收款账户": bank_account,
                "打款账户": bank_account,
                "联系人": contact_name,
                "联系人姓名": contact_name,
                "联系电话": contact_phone,
                "手机号": contact_phone,
                "手机号码": contact_phone,
                "真实姓名": contact_name,
                "身份证号": id_card,
                "法人姓名": contact_name,
                "法人账户": bank_account,
                "法人支付宝账户": bank_account,
                "支付渠道": pay_channel,
                "打款渠道": pay_channel,
            },
        )
        saved = self.click_latest_confirm_primary_button(frame) or self.click_overlay_action(
            frame,
            ["提交", "保存", "确 定", "确定", "确认"],
        )
        assert saved, f"商户打款配置弹窗未找到保存/确认按钮；text={self.visible_modal_text(frame)[:1500]}"
        self.page.wait_for_timeout(3000)
        self.search_merchant_if_possible(frame, merchant_keyword)
        final_text = frame.locator("body").inner_text(timeout=5000)
        assert self.merchant_payment_config_ready(final_text, merchant_keyword), (
            f"保存商户打款配置后仍未看到已完善/启用状态；merchant={merchant_keyword}; text={final_text[:1500]}"
        )
        return final_text

    def open_merchant_payment_config(self) -> Frame:
        try:
            return self.open_owner_bill_payment_account()
        except AssertionError:
            pass

        self.open_paycenter_shell()
        for menu_text in ["商户支付信息", "商户打款配置", "商户支付配置", "商户信息", "打款配置"]:
            if (
                self.click_sidebar_leaf_strict(menu_text)
                or self.click_sidebar_leaf_precise(menu_text)
                or self.click_sidebar_text_scrolled(menu_text)
            ):
                self.page.wait_for_timeout(3500)
                self.retry_if_app_load_timeout()
                return self.best_paycenter_frame(["商户", "支付信息", "打款配置", "开户", "账号"])

        text = self.page.locator("body").inner_text(timeout=5000)
        raise AssertionError(f"未找到支付中心-商户支付信息/商户打款配置入口；text={text[:1500]}")

    def open_owner_bill_payment_account(self) -> Frame:
        self.goto(WANYOUJI_CONSOLE_URL)
        self.wait_for_enterprise_console_shell()
        clicked_parent = self.click_sidebar_text_scrolled("结算中心", right_edge=True)
        assert clicked_parent, (
            f"丸友集主账号未找到结算中心菜单；text={self.page.locator('body').inner_text(timeout=5000)[:1200]}"
        )
        self.page.wait_for_timeout(1200)
        clicked = self.click_sidebar_leaf_precise("结算账单-丸友集") or self.click_sidebar_text_scrolled("结算账单-丸友集")
        assert clicked, (
            f"丸友集主账号未找到结算中心-结算账单-丸友集；text={self.page.locator('body').inner_text(timeout=5000)[:1200]}"
        )
        self.page.wait_for_timeout(3500)
        return self.owner_settlement_frame("结算账单-丸友集")

    def open_paycenter_shell(self) -> None:
        self.goto(WANYOUJI_CONSOLE_URL)
        self.wait_for_enterprise_console_shell()
        clicked = False
        for menu_text in ["支付中心", "支付管理", "结算配置"]:
            clicked = (
                self.click_sidebar_visible_text(menu_text, right_edge=True)
                or self.click_sidebar_text_scrolled(menu_text, right_edge=True)
            )
            if clicked:
                break
        assert clicked, (
            f"未找到支付中心入口；text={self.page.locator('body').inner_text(timeout=5000)[:1500]}"
        )
        self.page.wait_for_timeout(1200)

    def best_paycenter_frame(self, keywords: list[str]) -> Frame:
        last_texts: list[str] = []
        for _ in range(8):
            frames = [frame for frame in self.page.frames if frame != self.page.main_frame]
            for frame in frames:
                try:
                    text = frame.locator("body").inner_text(timeout=1200)
                except Exception:
                    continue
                if any(keyword in text for keyword in keywords):
                    return frame
                if text.strip():
                    last_texts.append(f"{frame.url}: {text[:500]}")
            self.page.wait_for_timeout(1000)
        body_text = self.page.locator("body").inner_text(timeout=5000)
        raise AssertionError(
            f"未找到支付中心业务 iframe；keywords={keywords}; body={body_text[:1200]}; frames={last_texts[-5:]}"
        )

    def merchant_payment_config_ready(self, text: str, merchant_keyword: str) -> bool:
        if "打款账户信息" in text and "未完善" not in text and "修改账户信息" in text:
            return True
        if merchant_keyword and merchant_keyword not in text and "打款账户信息" not in text:
            return False
        if any(key in text for key in PAYCENTER_NOT_READY_TEXTS):
            return False
        return any(key in text for key in PAYCENTER_READY_TEXTS)

    def search_merchant_if_possible(self, frame: Frame, keyword: str) -> None:
        if not keyword:
            return
        self.reset_search_if_possible(frame)
        self.search_order_if_possible(frame, keyword)
        self.page.wait_for_timeout(1000)

    def open_merchant_payment_config_editor(self, frame: Frame) -> bool:
        for text in ["修改账户信息", "完善账户信息", "新增账户信息"]:
            try:
                locator = frame.get_by_text(text, exact=True).first
                expect(locator).to_be_visible(timeout=1500)
                locator.click(force=True)
                self.page.wait_for_timeout(1500)
                if self.visible_account_config_overlay(frame):
                    return True
            except Exception:
                pass

        box = frame.evaluate(
            """texts => {
                const nodes = Array.from(document.querySelectorAll('button, a, [role="button"], span, div, taro-view-core'))
                  .map(el => ({ el, text: (el.innerText || el.textContent || '').trim(), r: el.getBoundingClientRect(), disabled: el.disabled }))
                  .filter(item => item.r.width > 0 && item.r.height > 0 && !item.disabled)
                  .filter(item => texts.some(text => item.text === text || item.text.includes(text)))
                  .sort((a, b) => {
                    const exactA = texts.some(text => a.text === text) ? 0 : 1;
                    const exactB = texts.some(text => b.text === text) ? 0 : 1;
                    return exactA - exactB || (a.r.width * a.r.height) - (b.r.width * b.r.height);
                  });
                const target = nodes[0];
                if (!target) return null;
                target.el.scrollIntoView({ block: 'center', inline: 'center' });
                return { x: target.r.x + target.r.width / 2, y: target.r.y + target.r.height / 2, text: target.text };
            }""",
            ["修改账户信息", "完善账户信息", "新增账户信息"],
        )
        if box:
            frame_element = frame.frame_element()
            frame_box = frame_element.bounding_box()
            if frame_box:
                self.page.mouse.click(frame_box["x"] + box["x"], frame_box["y"] + box["y"])
                self.page.wait_for_timeout(1800)
                if self.visible_account_config_overlay(frame):
                    return True

        clicked = frame.evaluate(
            """texts => {
                const nodes = Array.from(document.querySelectorAll('button, a, [role="button"], span'))
                  .map(el => ({ el, text: (el.innerText || el.textContent || '').trim(), r: el.getBoundingClientRect(), disabled: el.disabled }))
                  .filter(item => item.r.width > 0 && item.r.height > 0 && !item.disabled)
                  .filter(item => texts.some(text => item.text === text || item.text.includes(text)))
                  .sort((a, b) => {
                    const exactA = texts.some(text => a.text === text) ? 0 : 1;
                    const exactB = texts.some(text => b.text === text) ? 0 : 1;
                    return exactA - exactB || (a.r.width * a.r.height) - (b.r.width * b.r.height);
                  });
                const target = nodes[0];
                if (!target) return false;
                const clickable = target.el.closest('button, a, [role="button"]') || target.el;
                clickable.click();
                return true;
            }""",
            ["新增", "新增商户支付信息", "新增打款配置", "完善", "去完善", "编辑", "修改"],
        )
        self.page.wait_for_timeout(1500)
        return bool(clicked) and self.visible_account_config_overlay(frame)

    def visible_account_config_overlay(self, frame: Frame) -> bool:
        return bool(
            frame.evaluate(
                """() => {
                    const roots = Array.from(document.querySelectorAll('.ant-modal, .ant-drawer'))
                      .filter(el => {
                        const r = el.getBoundingClientRect();
                        return r.width > 0 && r.height > 0;
                      });
                    const root = roots[roots.length - 1];
                    if (!root) return false;
                    const text = root.innerText || root.textContent || '';
                    return text.includes('账户') || text.includes('打款') || text.includes('收款');
                }"""
            )
        )

    def fill_merchant_payment_config_form(self, frame: Frame, defaults: dict[str, str]) -> None:
        frame.evaluate(
            """defaults => {
                const roots = Array.from(document.querySelectorAll('.ant-modal, .ant-drawer, form'))
                  .filter(el => {
                    const r = el.getBoundingClientRect();
                    return r.width > 0 && r.height > 0;
                  });
                const root = roots[roots.length - 1] || document;
                const controls = Array.from(root.querySelectorAll('input, textarea'))
                  .map(el => {
                    const r = el.getBoundingClientRect();
                    const placeholder = el.getAttribute('placeholder') || '';
                    let label = placeholder;
                    for (let node = el.parentElement; node && node !== root; node = node.parentElement) {
                      const text = (node.innerText || node.textContent || '').replace(/\\s+/g, '');
                      if (text && text.length < 100) {
                        label = `${label} ${text}`;
                        break;
                      }
                    }
                    return { el, r, placeholder, label };
                  })
                  .filter(item => item.r.width > 0 && item.r.height > 0)
                  .filter(item => !item.el.disabled && !item.el.readOnly && item.el.type !== 'hidden');
                const setValue = (el, value) => {
                  const proto = el.tagName === 'TEXTAREA'
                    ? window.HTMLTextAreaElement.prototype
                    : window.HTMLInputElement.prototype;
                  const setter = Object.getOwnPropertyDescriptor(proto, 'value').set;
                  setter.call(el, value);
                  el.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertText', data: value }));
                  el.dispatchEvent(new Event('change', { bubbles: true }));
                  el.dispatchEvent(new FocusEvent('blur', { bubbles: true }));
                };
                const used = new Set();
                for (const [key, value] of Object.entries(defaults)) {
                  const target = controls.find(item =>
                    !used.has(item.el) &&
                    (item.placeholder.includes(key) || item.label.includes(key))
                  );
                  if (!target) continue;
                  setValue(target.el, value);
                  used.add(target.el);
                }
            }""",
            defaults,
        )
        self.page.wait_for_timeout(800)
        self.select_visible_dropdown_options(frame)

    def select_visible_dropdown_options(self, frame: Frame) -> None:
        frame.evaluate(
            """() => {
                const roots = Array.from(document.querySelectorAll('.ant-modal, .ant-drawer, form'))
                  .filter(el => {
                    const r = el.getBoundingClientRect();
                    return r.width > 0 && r.height > 0;
                  });
                const root = roots[roots.length - 1] || document;
                const selects = Array.from(root.querySelectorAll('.ant-select-selector'))
                  .map(el => ({ el, text: (el.innerText || el.textContent || '').trim(), r: el.getBoundingClientRect() }))
                  .filter(item => item.r.width > 0 && item.r.height > 0 && (!item.text || item.text.includes('请选择')));
                for (const item of selects.slice(0, 3)) item.el.click();
            }"""
        )
        self.page.wait_for_timeout(500)
        frame.evaluate(
            """() => {
                const options = Array.from(document.querySelectorAll('.ant-select-item-option:not(.ant-select-item-option-disabled)'))
                  .map(el => ({ el, r: el.getBoundingClientRect() }))
                  .filter(item => item.r.width > 0 && item.r.height > 0);
                for (const item of options.slice(0, 3)) item.el.click();
            }"""
        )
        self.page.wait_for_timeout(500)
