from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import List

import yaml

from .util import safe_copy, safe_write_text


class FileVersion:
    def __init__(
            self,
            date: datetime = None,
            filename: str = None,
            author: str = None,
            is_approve: bool = None,
            is_backup: bool = None,
            dir_path: Path = None,
            st_size: int = None,
            cmf_deleted: bool = False,
            version: int = 0
    ):
        # В старых версиях нет оффсета, считаем что там был utc
        if not date.tzinfo:
            date = date.replace(tzinfo=timezone.utc)
        self.date = date
        self.filename = filename
        self.author = author
        self.is_approve = is_approve
        self.is_backup = is_backup
        self.dir_path = dir_path
        self.st_size = st_size
        self.cmf_deleted = cmf_deleted
        self.version = version

    dump_fieldnames = ['author', 'date', 'filename', 'is_approve', 'is_backup', 'cmf_deleted', 'st_size', 'version']

    def to_pyobj(self):
        return {k: v for k, v in vars(self).items() if k in self.dump_fieldnames}

    @property
    def path(self):
        return self.dir_path / self.filename


class FileVersions:
    def __init__(self, file_path: Path = None, versions: List[FileVersion] = None):
        if not versions:
            versions = []
        self.versions = versions
        self.file_path = file_path

    @property
    def version_file_path(self) -> Path:
        return Path(str(self.file_path) + ".meta") / "versions.yaml"

    @property
    def versions_dir_path(self) -> Path:
        return Path(str(self.file_path) + ".meta") / "versions"

    @classmethod
    def for_file_path(cls, file_path: Path) -> "FileVersions":
        fvs = cls(file_path=file_path)

        fvs.version_file_path.touch(exist_ok=True)
        fvs.versions_dir_path.mkdir(exist_ok=True)

        versions = []

        versions_file_data = fvs.version_file_path.read_bytes()

        if versions_file_data:
            for v in yaml.safe_load(versions_file_data):
                if v.get('cmf_deleted'):
                    continue
                versions.append(FileVersion(**v, dir_path=fvs.versions_dir_path))
        versions.sort(key=lambda v: v.version)
        fvs.versions = versions
        return fvs

    def last_version_date(self):
        if not self.versions:
            return None

        return max(i.date for i in self.versions if not i.is_approve)

    def backup(self, **kwargs):
        date = datetime.now(timezone.utc).replace(microsecond=0)
        filename = date.strftime("%Y%m%d%H%M%S") + '-' + self.file_path.name
        new_version = FileVersion(
            date=date,
            filename=filename,
            author=kwargs.get("author", None),
            is_approve=False,
            is_backup=True,
            dir_path=self.versions_dir_path
        )
        safe_copy(self.file_path, new_version.path)
        new_version.st_size = new_version.path.stat().st_size
        if not self.versions:
            new_version.version = 1
        else:
            new_version.version = self.versions[-1].version + 1
        self.versions.append(new_version)
        self.dump_versions_file()

    def backup_smart(self, **kwargs):
        last_version_date = self.last_version_date()
        if last_version_date and (datetime.now(timezone.utc) - last_version_date < timedelta(minutes=30)):
            return
        self.backup(**kwargs)

    def dump_versions_file(self):
        pyobj = [i.to_pyobj() for i in self.versions]
        data = yaml.safe_dump(pyobj, allow_unicode=True)
        safe_write_text(data, self.version_file_path)

    def restore(self, version: FileVersion):
        src = version.path
        dst = self.file_path
        safe_copy(src, dst)

    def remove(self, version: FileVersion):
        """Фейковое удаление, просто не загружаем в список"""
        version.cmf_deleted = True
        self.dump_versions_file()
