import pyotherside  # pyright: ignore reportMissingImports
from decorators import log_error
from PIL import Image, ImageDraw, ImageChops
from PIL.Image import Image as ImageType
from io import BytesIO
from glob import glob
from collections import Counter
from subprocess import getstatusoutput
from urllib.parse import urlparse
from urllib.parse import urljoin
from functools import lru_cache
from lxml import etree
import requests
import base64
import os
import shutil
import logging
import tempfile

ICON_SIZE = (256, 256)

HEADERS = {
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0"
}

logging_filename = os.path.join(os.path.expanduser("~"),
                                ".local",
                                "share",
                                "dev.kirbylife",
                                "harbour-muchkin",
                                "muchkin.log")
os.makedirs(os.path.dirname(logging_filename), exist_ok=True)
logging.basicConfig(filename=logging_filename, filemode="w", level=logging.DEBUG)

@lru_cache(maxsize=50)
def download_icon(url) -> ImageType:
    response = requests.get(url)
    img = Image.open(BytesIO(response.content))
    img.thumbnail(ICON_SIZE)
    img = img.convert("RGBA")
    return img

@lru_cache(maxsize=50)
def base64_to_img(base64_bytes: bytes) -> ImageType:
    img_bytes = base64.b64decode(base64_bytes)
    img_buffer = BytesIO(img_bytes)
    img = Image.open(img_buffer)
    img.thumbnail(ICON_SIZE)
    img = img.convert("RGBA")
    return img

@lru_cache(maxsize=50)
def svg_to_img(svg_url: str) -> ImageType:
    response = requests.get(svg_url)
    with tempfile.TemporaryDirectory() as d:
        with open(os.path.join(d, "icon.svg"), "w") as f:
            f.write(response.text)
        status, output = getstatusoutput(f"sailfish_svg2png -z 16 {d} {d}")
        logging.info("Converted svg to png")
        logging.info(f"sailfish_svg2png status {status}: {output}")

        img = Image.open(os.path.join(d, "icon.png"))
        img.thumbnail(ICON_SIZE)
        img = img.convert("RGBA")
    return img

def avg_colors(img):
    counter = Counter()
    width, height = img.size
    for x in range(width):
        for y in range(height):
            pixel = img.getpixel((x, y))
            if pixel[-1] > 0: # Doesn't count the transparent pixels
                counter[pixel] += 1
    return counter.most_common(1)[0][0]

def add_background(img: ImageType, bg_tuple: tuple, size: float) -> ImageType:
    img = img.copy()
    size = max(size, 1) / 100
    bg = Image.new("RGBA", img.size, bg_tuple)
    img.thumbnail((value * size for value in img.size))
    bg.paste(img, ((bg.size[0] - img.size[0]) // 2, (bg.size[1] - img.size[1]) // 2), img)
    return bg

def crop_to_circle(im, template):
    im = im.copy()
    bigsize = (im.size[0] * 3, im.size[1] * 3)
    mask = Image.new('L', bigsize, 0)
    ImageDraw.Draw(mask).ellipse((0, 0) + bigsize, fill=255)
    midsize = tuple(x//2 for x in bigsize)
    if template[0] == 1:
        ImageDraw.Draw(mask).rectangle((0, 0) + midsize, fill=255)
    if template[1] == 1:
        ImageDraw.Draw(mask).rectangle((midsize[0], 0, bigsize[0], midsize[1]), fill=255)
    if template[2] == 1:
        ImageDraw.Draw(mask).rectangle((0, midsize[1], midsize[1], bigsize[1]), fill=255)
    if template[3] == 1:
        ImageDraw.Draw(mask).rectangle(midsize + bigsize, fill=255)
    mask = mask.resize(im.size, Image.ANTIALIAS)
    mask = ImageChops.darker(mask, im.split()[-1])
    im.putalpha(mask)
    return im

def parse_file(desktop_path):
    output = {}
    with open(desktop_path, "r") as f:
        for line in f:
            if not line or line.startswith("["):
                continue
            key, value = line.split("=", 1)
            output[key] = value.strip()
    output["path"] = desktop_path
    return output

def deparse_file(app):
    output = ["[Desktop Entry]"]
    for key, value in app.items():
        output.append(f"{key}={value}")
    return "\n".join(output)

def get_web_icons():
    path = os.path.join(
        os.path.expanduser("~"),
        ".local",
        "share",
        "applications",
        "sailfish-browser*.desktop"
    )
    apps = glob(path)

    return list(map(parse_file, apps))

@log_error
def scrape_icon(url: str) -> str:
    url_parsed = urlparse(url)
    origin = f"{url_parsed.scheme}://{url_parsed.netloc}"
    html = etree.HTML(requests.get(origin, headers=HEADERS).text)

    possible_svg = None
    possible_icon = None
    max_size = 0

    for icon in html.xpath("//link[contains(@rel, 'icon')]"):
        if "SVG" in next(iter(icon.xpath("@type")), "").upper():
            possible_svg = icon.xpath("@href")[0]
            break
        possible_href = next(iter(icon.xpath("@href")), None)
        possible_size = next(iter(icon.xpath("@sizes")), None)
        possible_rel = next(iter(icon.xpath("@rel")), "")
        if possible_size and possible_href and possible_size != "any":
            size = int(possible_size.split("x")[0])
            if size > max_size:
                max_size = size
                possible_icon = possible_href
        if  possible_href and not possible_size and possible_rel.upper() == "apple-touch-icon".upper():
            possible_icon = possible_href

    new_icon = possible_svg or possible_icon
    if new_icon:
        parsed_new_icon = urlparse(new_icon)
        if not parsed_new_icon.scheme or not parsed_new_icon.netloc:
            new_icon = urljoin(origin, new_icon)
        return sailify(new_icon, [True] * 4, 100)

@log_error
def sailify(raw_str, pattern, size) -> str:
    url_parsed = urlparse(raw_str)
    if url_parsed.scheme == "data":
        # The old ones converted the icon to base64
        base64_bytes = raw_str.replace("data:image/png;base64,", "", 1).encode()
        img = base64_to_img(base64_bytes)
        logging.info("Converted from base64")
    # New versions of Sailfish has the url on the icons
    elif url_parsed.path.split("/")[-1].split(".")[-1].upper() == "SVG":
        img = svg_to_img(raw_str)
        logging.info("Downloaded from url: " + raw_str)
    else:
        img = download_icon(raw_str)
        logging.info("Downloaded from url: " + raw_str)

    bg_tuple = avg_colors(img)
    img = add_background(img, bg_tuple, size)
    img = crop_to_circle(img, pattern)
    output_buffer = BytesIO()
    img.save(output_buffer, format="PNG")
    output_data = output_buffer.getvalue()
    output_base64 = base64.b64encode(output_data)
    return "data:image/png;base64," + output_base64.decode()

def backup_icon(app):
    if not os.path.exists(app["path"] + "_backup"):
        backup_path = app["path"] + "_backup"
        shutil.copyfile(app["path"], backup_path)
        logging.info("Icon backed up on: " + backup_path)
    else:
        logging.info("Backup already exists")

def save_icon(app):
    new_content = deparse_file(app)
    with open(app["path"], "w") as f:
        f.write(new_content)
    logging.info("Icon saved on: " + app["path"])
    return True

def restore_icon(app):
    if "old_icon" not in app:
        return False

    app["Icon"] = app["old_icon"]
    del app["old_icon"]

    new_content = deparse_file(app)
    with open(app["path"], "w") as f:
        f.write(new_content)
    logging.info("Icon saved on: " + app["path"])
    return True
