# Copyright (c) Mengning Software. 2023. All rights reserved.
#
# Super IDE licensed under GNU Affero General Public License v3 (AGPL-3.0) .
# You can use this software according to the terms and conditions of the AGPL-3.0.
# You may obtain a copy of AGPL-3.0 at:
#
#    https://www.gnu.org/licenses/agpl-3.0.txt
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the AGPL-3.0 for more details.

import glob
import io
import os
import re
import shutil
from functools import cmp_to_key

import click

from superide import fs,proc
from superide.cache import ContentCache
from superide.compat import IS_WINDOWS
# from superide.device.list.util import list_logical_devices
from superide.home.rpc.handlers.base import BaseRPCHandler
from superide.http import HTTPSession, ensure_internet_on


# class HTTPAsyncSession(HTTPSession):
#     # async def request(  # pylint: disable=signature-differs,invalid-overridden-method
#     #     self, *args, **kwargs
#     # ):
#     #     func = super().request
#     #     return await aio_to_thread(func, *args, **kwargs)


class OSRPC():
    _http_session = None

    @classmethod
    async def fetch_content(cls, url, data=None, headers=None, cache_valid=None):
        if not headers:
            headers = {
                "User-Agent": (
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) "
                    "AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 "
                    "Safari/603.3.8"
                )
            }
        cache_key = ContentCache.key_from_args(url, data) if cache_valid else None
        with ContentCache() as cc:
            if cache_key:
                result = cc.get(cache_key)
                if result is not None:
                    return result

        # check internet before and resolve issue with 60 seconds timeout
        ensure_internet_on(raise_exception=True)

        if not cls._http_session:
            cls._http_session = HTTPAsyncSession()

        if data:
            r = await cls._http_session.post(url, data=data, headers=headers)
        else:
            r = await cls._http_session.get(url, headers=headers)

        r.raise_for_status()
        result = r.text
        if cache_valid:
            with ContentCache() as cc:
                cc.set(cache_key, result, cache_valid)
        return result

    async def request_content(self, uri, data=None, headers=None, cache_valid=None):
        if uri.startswith("http"):
            return await self.fetch_content(uri, data, headers, cache_valid)
        local_path = uri[7:] if uri.startswith("file://") else uri
        with io.open(local_path, encoding="utf-8") as fp:
            return fp.read()
        return None

    @staticmethod
    def open_url(url):
        return click.launch(url)

    @staticmethod
    def reveal_file(path):
        return click.launch(path, locate=True)

    @staticmethod
    def open_file(path):
        return click.launch(path)

    @staticmethod
    def call_path_module_func(name, args, **kwargs):
        return getattr(os.path, name)(*args, **kwargs)

    @staticmethod
    def get_path_separator():
        return os.sep

    @staticmethod
    def is_file(path):
        return os.path.isfile(path)

    @staticmethod
    def is_dir(path):
        return os.path.isdir(path)

    @staticmethod
    def make_dirs(path):
        return os.makedirs(path)

    @staticmethod
    def get_file_mtime(path):
        return os.path.getmtime(path)

    @staticmethod
    def rename(src, dst):
        return os.rename(src, dst)

    @staticmethod
    def copy(src, dst):
        return shutil.copytree(src, dst, symlinks=True)

    @staticmethod
    def glob(pathnames, root=None):
        if not isinstance(pathnames, list):
            pathnames = [pathnames]
        result = set()
        for pathname in pathnames:
            result |= set(
                glob.glob(
                    os.path.join(root, pathname) if root else pathname, recursive=True
                )
            )
        return list(result)

    @staticmethod
    def list_dir(path):
        def _cmp(x, y):
            if x[1] and not y[1]:
                return -1
            if not x[1] and y[1]:
                return 1
            if x[0].lower() > y[0].lower():
                return 1
            if x[0].lower() < y[0].lower():
                return -1
            return 0

        items = []
        if path.startswith("~"):
            path = fs.expanduser(path)
        if not os.path.isdir(path):
            return items
        for item in os.listdir(path):
            try:
                item_is_dir = os.path.isdir(os.path.join(path, item))
                if item_is_dir:
                    os.listdir(os.path.join(path, item))
                items.append((item, item_is_dir))
            except OSError:
                pass
        return sorted(items, key=cmp_to_key(_cmp))

    @staticmethod
    def get_logical_devices():
        items = []
        if IS_WINDOWS:
            try:
                result = proc.exec_command(
                    ["wmic", "logicaldisk", "get", "name,VolumeName"]
                ).get("out", "")
                devicenamere = re.compile(r"^([A-Z]{1}\:)\s*(\S+)?")
                for line in result.split("\n"):
                    match = devicenamere.match(line.strip())
                    if not match:
                        continue
                    items.append({"path": match.group(1) + "\\", "name": match.group(2)})
                return items
            except OSError:  # pylint: disable=undefined-variable
                pass
            # try "fsutil"
            result = proc.exec_command(["fsutil", "fsinfo", "drives"]).get("out", "")
            for device in re.findall(r"[A-Z]:\\", result):
                items.append({"path": device, "name": None})
            return items

        result = proc.exec_command(["df"]).get("out")
        devicenamere = re.compile(r"^/.+\d+\%\s+([a-z\d\-_/]+)$", flags=re.I)
        for line in result.split("\n"):
            match = devicenamere.match(line.strip())
            if not match:
                continue
            items.append({"path": match.group(1), "name": os.path.basename(match.group(1))})
        return items
