from __future__ import annotations

import random
import re
import subprocess
import sys
import time
import types
from pathlib import Path
from typing import Any, Dict, List
from urllib.parse import quote

try:
    import undetected_chromedriver as uc
except ModuleNotFoundError:
    try:
        from setuptools._distutils import version as _distutils_version

        distutils_module = types.ModuleType("distutils")
        distutils_module.version = _distutils_version
        sys.modules.setdefault("distutils", distutils_module)
        sys.modules.setdefault("distutils.version", _distutils_version)
        import undetected_chromedriver as uc
    except Exception:
        uc = None

from openpyxl import load_workbook
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait


class WhatsAppService:
    def __init__(self):
        self.driver = None
        self.session_status = "not_connected"
        self.user_data_dir = Path(__file__).resolve().parents[2] / "data" / "user_data"
        self.user_data_dir.mkdir(parents=True, exist_ok=True)

    def has_existing_session(self) -> bool:
        try:
            session_file = self.user_data_dir / "Default" / "IndexedDB"
            return session_file.exists()
        except Exception:
            return False

    def detect_chrome_version(self) -> int | None:
        candidates = [
            Path(r"C:\Program Files\Google\Chrome\Application\chrome.exe"),
            Path(r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"),
            Path.home() / "AppData" / "Local" / "Google" / "Chrome" / "Application" / "chrome.exe",
        ]

        for candidate in candidates:
            if not candidate.exists():
                continue
            try:
                ps_command = "(Get-Item -LiteralPath '" + str(candidate).replace("'", "''") + "').VersionInfo.FileVersion"
                result = subprocess.run(
                    ["powershell", "-NoProfile", "-Command", ps_command],
                    capture_output=True,
                    text=True,
                    timeout=15,
                    check=False,
                )
                output = (result.stdout or result.stderr or "").strip()
                match = re.search(r"(\d+)\.(\d+)\.(\d+)\.(\d+)", output) or re.search(r"(\d+)\.(\d+)\.(\d+)", output)
                if match:
                    return int(match.group(1))
            except Exception:
                continue

        return None

    def initialize_driver(self, headless: bool = False) -> bool:
        if uc is None:
            return False

        try:
            if self.driver is not None:
                self.close()
                time.sleep(1)

            options = uc.ChromeOptions()
            options.add_argument(f"--user-data-dir={self.user_data_dir}")
            options.add_argument("--no-first-run")
            options.add_argument("--no-default-browser-check")
            options.add_argument("--disable-blink-features=AutomationControlled")
            options.add_argument("--disable-extensions")
            options.add_argument("--disable-default-apps")
            options.add_argument("--disable-gpu")
            options.add_argument("--disable-dev-shm-usage")
            options.add_argument("--window-size=1400,1200")

            if headless:
                options.add_argument("--headless=new")

            chrome_version = self.detect_chrome_version()
            if chrome_version:
                self.driver = uc.Chrome(options=options, version_main=chrome_version)
            else:
                self.driver = uc.Chrome(options=options)

            time.sleep(2)
            return True
        except Exception:
            self.driver = None
            return False

    def navigate_to_whatsapp(self) -> bool:
        if self.driver is None:
            return False
        try:
            self.driver.get("https://web.whatsapp.com/")
            time.sleep(3)
            return True
        except Exception:
            return False

    def wait_for_session(self, timeout: int = 60) -> bool:
        if self.driver is None:
            return False
        try:
            wait = WebDriverWait(self.driver, timeout)
            wait.until(lambda d: "web.whatsapp.com" in d.current_url.lower())
            return True
        except TimeoutException:
            return False

    def reconnect_session(self, timeout: int = 60) -> bool:
        try:
            self.close()
            time.sleep(1)
            if not self.initialize_driver(headless=False):
                return False
            if not self.navigate_to_whatsapp():
                return False
            return self.wait_for_session(timeout=timeout)
        except Exception:
            return False

    def close(self):
        if self.driver is not None:
            try:
                self.driver.quit()
            finally:
                self.driver = None

    def connect(self) -> str:
        self.session_status = "starting"
        try:
            if not self.initialize_driver(headless=False):
                self.session_status = "not_connected"
                return self.session_status

            if not self.navigate_to_whatsapp():
                self.session_status = "not_connected"
                return self.session_status

            if self.wait_for_session(timeout=30):
                self.session_status = "connected"
            else:
                self.session_status = "not_connected"
        except Exception:
            self.driver = None
            self.session_status = "not_connected"

        return self.session_status

    def validate(self) -> str:
        if self.driver is None:
            self.session_status = "not_connected"
            return self.session_status

        try:
            url = (self.driver.current_url or "").lower()
            if "web.whatsapp.com" in url:
                self.session_status = "connected"
                return self.session_status
        except Exception:
            pass

        self.session_status = "not_connected"
        return self.session_status

    def send_via_whatsapp_web(self, phone: str, message: str) -> tuple[bool, str]:
        """Envia un mensaje real usando el editor de WhatsApp Web, no un enlace de API."""
        if self.driver is None:
            return False, "Sesión no activa"

        try:
            phone_clean = re.sub(r"[^0-9]", "", str(phone or ""))
            if not phone_clean:
                return False, "Teléfono inválido"

            wait = WebDriverWait(self.driver, 20)
            body = self.driver.find_element(By.TAG_NAME, "body")
            body.send_keys(Keys.CONTROL, Keys.ALT, "n")
            time.sleep(1.5)

            search_input = wait.until(
                EC.presence_of_element_located(
                    (
                        By.XPATH,
                        "//input[@placeholder='Search or start a new chat' or contains(@placeholder, 'Search') or @type='text']",
                    )
                )
            )
            search_input.clear()
            search_input.send_keys(phone_clean)
            time.sleep(2)

            try:
                search_input.send_keys(Keys.ENTER)
                time.sleep(1.5)
            except Exception:
                pass

            message_box = wait.until(
                EC.presence_of_element_located(
                    (By.XPATH, "//div[@contenteditable='true' and @data-tab='1'] | //div[@contenteditable='true'] | //textarea")
                )
            )
            message_box.click()
            time.sleep(0.5)
            message_box.send_keys(message)
            time.sleep(0.5)
            message_box.send_keys(Keys.ENTER)
            time.sleep(1)
            return True, "Enviado correctamente"
        except (TimeoutException, NoSuchElementException) as exc:
            return False, f"No se pudo abrir el chat o el editor: {exc}"
        except Exception as exc:
            return False, str(exc)

    def send_campaign(self, message: str, contacts: List[Dict[str, Any]]) -> Dict[str, Any]:
        if self.driver is None:
            return {"status": "error", "sent": 0, "failed": len(contacts), "message": "Sesión no activa"}

        MAX_PER_CAMPAIGN = 100
        limited_contacts = contacts[:MAX_PER_CAMPAIGN]
        sent = 0
        failed = 0

        for index, contact in enumerate(limited_contacts):
            name = str(contact.get("Nombre") or contact.get("nombre") or "Cliente").strip()
            phone = str(contact.get("Telefono") or contact.get("telefono") or "").strip()
            if not phone:
                failed += 1
                continue

            rendered_message = message.replace("{Nombre}", name)
            try:
                ok, _ = self.send_via_whatsapp_web(phone, rendered_message)
                if ok:
                    sent += 1
                else:
                    failed += 1
            except Exception:
                failed += 1

            if index < len(limited_contacts) - 1:
                delay = random.uniform(5, 9)
                time.sleep(delay)

        return {
            "status": "ok",
            "sent": sent,
            "failed": failed,
            "limit_applied": len(contacts) > MAX_PER_CAMPAIGN,
            "message": f"Campaña ejecutada: {sent} enviados, {failed} fallidos (máximo 100 por campaña)",
        }

    @staticmethod
    def read_contacts(file_path: str | Path) -> List[Dict[str, Any]]:
        workbook = load_workbook(file_path, read_only=True, data_only=True)
        sheet = workbook.active

        rows = list(sheet.iter_rows(values_only=True))
        if not rows:
            return []

        headers = [str(cell).strip() if cell is not None else "" for cell in rows[0]]
        contacts: List[Dict[str, Any]] = []
        for row in rows[1:]:
            row_data = {headers[idx]: row[idx] if idx < len(row) else "" for idx in range(len(headers))}
            if not any(str(value or "").strip() for value in row_data.values()):
                continue
            contacts.append(row_data)

        return contacts
