import base64
import math
import re
from typing import Any, Union
from urllib.parse import urljoin

import bs4

from .constants import (
    MIGRATION_REDIRECTION_REGEX,
    ON_DEMAND_FILE_REGEX,
    ON_DEMAND_FILE_URL,
)


class Math:

    @staticmethod
    def round(num: Union[float, int]):
        # using javascript...? just use the native Math.round(num)
        x = math.floor(num)
        if (num - x) >= 0.5:
            x = math.ceil(num)
        return math.copysign(x, num)


def generate_headers(user_agent: str = "", accept_language: str = ""):
    resolved_user_agent = (user_agent or "").strip()
    resolved_accept_language = (accept_language or "").strip()
    headers = {
        "authority": "x.com",
        "accept-language": resolved_accept_language or "en-US,en;q=0.9",
        "cache-control": "no-cache",
        "referer": "https://x.com",
        "user-agent": resolved_user_agent or (
            "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 "
            "(KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36"
        ),
        "x-twitter-active-user": "yes",
        "x-twitter-client-language": "en",
    }
    return headers


def validate_response(response: bs4.BeautifulSoup):
    if not isinstance(response, bs4.BeautifulSoup):
        raise TypeError(
            f"the response object must be bs4.BeautifulSoup, not {type(response).__name__}")


def get_migration_url(response: bs4.BeautifulSoup):
    migration_url = response.select_one("meta[http-equiv='refresh']")
    migration_redirection_url = re.search(MIGRATION_REDIRECTION_REGEX, str(
        migration_url)) or re.search(MIGRATION_REDIRECTION_REGEX, str(response.content))
    return migration_redirection_url


def get_migration_form(response: bs4.BeautifulSoup):
    migration_form = response.select_one("form[name='f']") or response.select_one(
        f"form[action='https://x.com/x/migrate']")
    if not migration_form:
        return
    url = migration_form.attrs.get("action", "https://x.com/x/migrate")
    method = migration_form.attrs.get("method", "POST")
    request_payload = {input_field.get("name"): input_field.get(
        "value") for input_field in migration_form.select("input")}
    return {"method": method, "url": url, "data": request_payload}


def get_ondemand_file_url(response: bs4.BeautifulSoup):
    file_url = None
    on_demand_file = ON_DEMAND_FILE_REGEX.search(str(response))
    if on_demand_file:
        filename = on_demand_file.group(1)
        file_url = ON_DEMAND_FILE_URL.format(filename=filename)
    return file_url


_REDIRECT_STATUS_CODES = {301, 302, 303, 307, 308}


def _is_redirect_loop_error(exc: Exception) -> bool:
    message = f"{type(exc).__name__}: {exc}"
    lowered = message.lower()
    return (
        "toomanyredirects" in lowered
        or "too many redirects" in lowered
        or "is_redirect error" in lowered
    )


def _fetch_with_manual_redirects(session, url: str, *, max_redirects: int = 5):
    current_url = url
    redirects_followed = 0
    while True:
        response = session.request(
            method="GET", url=current_url, allow_redirects=False
        )
        status = getattr(response, "status_code", None)
        if status not in _REDIRECT_STATUS_CODES:
            return response
        location = None
        headers = getattr(response, "headers", None)
        if headers:
            location = headers.get("location") or headers.get("Location")
        if not location:
            return response
        redirects_followed += 1
        if redirects_followed > max_redirects:
            raise RuntimeError(
                f"Redirect zinciri {max_redirects} sınırını aştı: {current_url}"
            )
        current_url = urljoin(current_url, location)


def _request_home_page(session, url: str) -> Any:
    try:
        return session.request(method="GET", url=url)
    except Exception as exc:
        if not _is_redirect_loop_error(exc):
            raise
        return _fetch_with_manual_redirects(session, url)


async def _fetch_with_manual_redirects_async(session, url: str, *, max_redirects: int = 5):
    current_url = url
    redirects_followed = 0
    while True:
        response = await session.request(
            method="GET", url=current_url, allow_redirects=False
        )
        status = getattr(response, "status_code", None)
        if status not in _REDIRECT_STATUS_CODES:
            return response
        location = None
        headers = getattr(response, "headers", None)
        if headers:
            location = headers.get("location") or headers.get("Location")
        if not location:
            return response
        redirects_followed += 1
        if redirects_followed > max_redirects:
            raise RuntimeError(
                f"Redirect zinciri {max_redirects} sınırını aştı: {current_url}"
            )
        current_url = urljoin(current_url, location)


async def _request_home_page_async(session, url: str) -> Any:
    try:
        return await session.request(method="GET", url=url)
    except Exception as exc:
        if not _is_redirect_loop_error(exc):
            raise
        return await _fetch_with_manual_redirects_async(session, url)


def handle_x_migration(session):
    # for python requests -> session = requests.Session()
    # session.headers = generate_headers()
    response = _request_home_page(session, "https://x.com")
    home_page = bs4.BeautifulSoup(response.content, 'html.parser')
    migration_redirection_url = get_migration_url(response=home_page)
    if migration_redirection_url:
        response = session.request(
            method="GET", url=migration_redirection_url.group(0))
        home_page = bs4.BeautifulSoup(response.content, 'html.parser')
    migration_form = get_migration_form(response=home_page)
    if migration_form:
        response = session.request(**migration_form)
        home_page = bs4.BeautifulSoup(response.content, 'html.parser')
    return home_page


async def handle_x_migration_async(session):
    # for httpx -> session = httpx.AsyncClient(headers=generate_headers())
    response = await _request_home_page_async(session, "https://x.com")
    home_page = bs4.BeautifulSoup(response.content, 'html.parser')
    migration_redirection_url = get_migration_url(response=home_page)
    if migration_redirection_url:
        response = await session.request(method="GET", url=migration_redirection_url.group(0))
        home_page = bs4.BeautifulSoup(response.content, 'html.parser')
    migration_form = get_migration_form(response=home_page)
    if migration_form:
        response = await session.request(**migration_form)
        home_page = bs4.BeautifulSoup(response.content, 'html.parser')
    return home_page


def float_to_hex(x):
    result = []
    quotient = int(x)
    fraction = x - quotient

    while quotient > 0:
        quotient = int(x / 16)
        remainder = int(x - (float(quotient) * 16))

        if remainder > 9:
            result.insert(0, chr(remainder + 55))
        else:
            result.insert(0, str(remainder))

        x = float(quotient)

    if fraction == 0:
        return ''.join(result)

    result.append('.')

    while fraction > 0:
        fraction *= 16
        integer = int(fraction)
        fraction -= float(integer)

        if integer > 9:
            result.append(chr(integer + 55))
        else:
            result.append(str(integer))

    return ''.join(result)


def is_odd(num: Union[int, float]):
    if num % 2:
        return -1.0
    return 0.0


def base64_encode(string: str):
    string = string.encode() if isinstance(string, str) else string
    return base64.b64encode(string).decode()


def base64_decode(input):
    try:
        data = base64.b64decode(input)
        return data.decode()
    except Exception:
        # return bytes(input, "utf-8")
        return list(bytes(input, "utf-8"))


if __name__ == "__main__":
    pass
