from core.Constants import Constants
from core.models.Location import Location
from core.models.Subscription import Subscription
from core.models.session.ApplicationVersion import ApplicationVersion
from dataclasses import dataclass, field, asdict
from dataclasses_json import config, Exclude, dataclass_json
from json import JSONDecodeError
from typing import Optional, Self
import json
import os
import re
import shutil


@dataclass_json
@dataclass
class BaseProfile:
    id: int = field(
        metadata=config(exclude=Exclude.ALWAYS)
    )
    name: str
    subscription: Optional[Subscription]
    location: Optional[Location]

    def get_config_path(self):
        return BaseProfile.__get_config_path(self.id)

    def get_data_path(self):
        return BaseProfile.__get_data_path(self.id)

    def has_subscription(self):
        return self.subscription is not None

    def has_location(self):
        return self.location is not None

    def is_session_profile(self):
        return type(self).__name__ == 'SessionProfile'

    def is_system_profile(self):
        return type(self).__name__ == 'SystemProfile'

    def save(self: Self):

        config_file_contents = f'{self.to_json(indent=4)}\n'

        os.makedirs(self.get_config_path(), exist_ok=True)
        os.makedirs(self.get_data_path(), exist_ok=True)

        config_file_path = f'{self.get_config_path()}/config.json'

        with open(config_file_path, 'w') as config_file:

            config_file.write(config_file_contents)
            config_file.close()

    def delete_data(self):
        shutil.rmtree(self.get_data_path(), ignore_errors=True)

    def delete(self):
        shutil.rmtree(self.get_config_path(), ignore_errors=True)
        self.delete_data()

    def _get_dirty_keys(self: Self):

        reference = BaseProfile.find_by_id(self.id)

        if type(reference) != type(self):
            return list(self.__dataclass_fields__.keys())

        return list([key for key, value in asdict(self).items() if value != asdict(reference).get(key)])

    @staticmethod
    def find_by_id(id: int):

        try:
            config_file_contents = open(f'{BaseProfile.__get_config_path(id)}/config.json', 'r').read()
        except FileNotFoundError:
            return None

        try:
            profile = json.loads(config_file_contents)
        except JSONDecodeError:
            return None

        profile['id'] = id

        if profile['location'] is not None:

            location = Location.find(profile['location']['country_code'] or None, profile['location']['code'] or None)

            if location is not None:

                if profile['location'].get('time_zone') is not None:
                    location.time_zone = profile['location']['time_zone']

                profile['location'] = location

        if 'application_version' in profile:

            if profile['application_version'] is not None:
                application_version = ApplicationVersion.find(profile['application_version']['application_code'] or None, profile['application_version']['version_number'] or None)

                if application_version is not None:
                    profile['application_version'] = application_version

            from core.models.session.SessionProfile import SessionProfile
            # noinspection PyUnresolvedReferences
            profile = SessionProfile.from_dict(profile)

        else:

            from core.models.system.SystemProfile import SystemProfile
            # noinspection PyUnresolvedReferences
            profile = SystemProfile.from_dict(profile)

        return profile

    @staticmethod
    def exists(id: int):
        return re.match(r'^\d{1,2}$', str(id)) and os.path.isfile(f'{BaseProfile.__get_config_path(id)}/config.json')

    @staticmethod
    def all():

        profiles = {}

        for directory_entry in os.listdir(Constants.HV_PROFILE_CONFIG_HOME):

            try:
                id = int(directory_entry)
            except ValueError:
                continue

            if BaseProfile.exists(id):

                profile = BaseProfile.find_by_id(id)

                if profile is not None:
                    profiles[id] = profile

        return dict(sorted(profiles.items()))

    @staticmethod
    def __get_config_path(id: int):
        return f'{Constants.HV_PROFILE_CONFIG_HOME}/{str(id)}'

    @staticmethod
    def __get_data_path(id: int):
        return f'{Constants.HV_PROFILE_DATA_HOME}/{str(id)}'