#!/usr/bin/env python3
"""
Locale handling for SolydXK
See https://docs.python.org/3/library/locale.html

Author: Arjen Balfoort
Date: 2026-01-31
Dependencies: locales
"""

import locale
import os
import re
import shutil
from typing import TypedDict
import apt


class LocaleInfo(TypedDict):
    """
    Locale information.
    
    Attributes:
        name (str): The human-readable language name.
        installed (bool): Whether the locale is installed or not.
        default (bool): Whether the locale is the default locale or not.
    """
    name: str
    installed: bool
    default: bool

class Localize():
    """ Handle localization. """
    def __init__(self):
        self._cache = apt.Cache()

    @staticmethod
    def _get_lang_name(loc: str) -> str:
        """
        Extract human-readable language name from locale file.
        Used in get_locales
        
        Args:
            loc (str): The locale code to get the language name for.

        Returns:
            str: The human-readable language name.
        """
        try:
            # Try to get the language name from the locale file
            with open(file=f"/usr/share/i18n/locales/{loc}", mode='r', encoding='utf-8') as f:
                match = re.search(r'lang_name\s+"(.*?)"', f.read())
                if match:
                    return match.group(1)
            # if not found, use the locale code as the language name
            return loc
        except FileNotFoundError:
            return loc

    @staticmethod
    def get_timezones():
        """
        Get timezones.

        Returns:
            dict: A dictionary with continent names as keys and a list of cities as values.
        """
        timezones = {}
        try:
            with open(file='/usr/share/zoneinfo/zone.tab', mode='r', encoding='utf-8') as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith('#'):
                        parts = line.split()
                        if len(parts) >= 3 and parts[2].count('/') == 1:
                            continent, city = parts[2].split('/')
                            if continent not in timezones:
                                timezones[continent] = []
                            timezones[continent].append(city)

            # 1. Sort each list of items alphabetically
            for _key, items in timezones.items():
                items.sort()

            # 2. Sort the dictionary by key
            sorted_timezones = dict(sorted(timezones.items()))

            # 3. Return the sorted dictionary
            return sorted_timezones
        except FileNotFoundError:
            return {}

    @staticmethod
    def get_current_timezone() -> list:
        """
        Get the current timezone.

        Returns:
            list: The current timezone.
        """
        # Check environment first
        tz = os.environ.get('TZ')
        if tz:
            return tz
        # Fall back to /etc/timezone
        try:
            with open(file='/etc/timezone', mode='r', encoding='utf-8') as f:
                return f.read().strip().split('/', 1)
        except FileNotFoundError:
            return []

    @staticmethod
    def _get_installed_locales(default_locale: str) -> list[str]:
        """
        Get installed locales.
        Used in get_locales

        Args:
            default_locale (str): The default locale code.

        Returns:
            list: A list of installed locale codes.
        """
        installed_locales = []
        installed_file = "/etc/locale.gen"

        # Create locale.gen if it doesn't exist
        if not os.path.exists(installed_file):
            with open(file=installed_file, mode='w', encoding='utf-8') as f:
                f.write(f"{default_locale}.UTF-8 UTF-8\n")

        # Get installed locales
        with open(file=installed_file, mode='r', encoding='utf-8') as f:
            for line in f.readlines():
                if line.startswith('#'):
                    continue
                # Skip lines that don't match the expected format
                match = re.match(r'^([a-z]{2}_[A-Z]{2})', line)
                if match:
                    installed_locales.append(match.group())

        return installed_locales

    @staticmethod
    def _get_supported_locales() -> list[str]:
        """
        Get supported locales.
        Used in get_locales

        Returns:
            list: A list of supported locale codes.
        """
        supported_locales = []
        supported_file = '/usr/share/i18n/SUPPORTED'

        if not os.path.exists(supported_file):
            return []

        with open(file=supported_file, mode='r', encoding='utf-8') as f:
            for line in f.readlines():
                # Skip lines that don't match the expected format
                match = re.match(r'^[a-z]{2}_[A-Z]{2}', line)
                if match:
                    supported_locales.append(match.group())

        # Remove dupliates
        supported_locales = sorted(list(set(supported_locales)))

        return supported_locales

    def get_locales(self) -> dict[str, LocaleInfo]:
        """
        List supported locales.

        Returns:
            dict: A dictionary with locale codes as keys 
                and a list of [language name, is installed, is default] as values.
        """
        try:
            # Get default locale from environment or system
            default_locale = os.environ.get('LANG', '').split('.')[0]
            if not default_locale:
                default_locale = locale.getlocale()[0]
        except (locale.Error, ValueError, AttributeError):
            default_locale = None

        # Get supported locales
        supported_locales = self._get_supported_locales()
        if not supported_locales:
            return {}

        # Installed locales variables
        installed_locales = self._get_installed_locales(default_locale)

        # Create dictionary
        locales = {}
        for loc in supported_locales:
            locales[loc] = {
                'name': self._get_lang_name(loc),
                'installed': loc in installed_locales,
                'default': loc == default_locale
            }

        return locales

    def set_locale(self, loc: str) -> None:
        """
        This function sets the locale and installs Grub language files.

        Args:
            loc (str): The locale code to set.
        """
        locale.setlocale(locale.LC_ALL, f"{loc}.UTF-8")



        # Install Grub language files
        grub_loc = '/boot/grub/locale'
        short_lang = loc.split('_')[0]
        mo = f"/usr/share/locale/{short_lang}/LC_MESSAGES/grub.mo"
        os.mkdir(grub_loc, exist_ok=True)
        shutil.copyfile(mo, f"{grub_loc}/{short_lang}.mo")

    def get_locale_packages(self, package_list: list[str], loc: str) -> list[str]:
        """
        Get locale packages.

        Args:
            package_list (list[str]): A list of package names.

        Returns:
            list[str]: A list of locale packages.
        """
        locale_packages = []
        # Format the locale to the package format for locale
        loc = loc.replace('_', '-')

        # Loop the given list
        for package_name in package_list:
            if package_name in self._cache:
                # Check if the locale package is available (ex: nl-nl)
                locale_name = f"{package_name}-l10n-{loc}"
                if locale_name not in self._cache:
                    # Check if the locale package is available (ex: nl)
                    loc = loc.split('-')[0]
                    locale_name = f"{package_name}-l10n-{loc}"
                if locale_name in self._cache:
                    locale_packages.append(locale_name)
        return locale_packages
