"""
This module provides the app's data store using Peewee with SQLite.

At the bottom there is a simple schema migration system.
"""

import json
from datetime import datetime
import peewee as pw
from playhouse import signals
from vorta.utils import slugify

DB = pw.Proxy()


class JSONField(pw.TextField):
    """
    Class to "fake" a JSON field with a text field. Not efficient but works nicely.

    From: https://gist.github.com/rosscdh/f4f26758b0228f475b132c688f15af2b
    """

    def db_value(self, value):
        """Convert the python value for storage in the database."""
        return value if value is None else json.dumps(value)

    def python_value(self, value):
        """Convert the database value to a pythonic value."""
        return value if value is None else json.loads(value)


class BaseModel(signals.Model):
    """Common model superclass."""


class RepoModel(BaseModel):
    """A single remote repo with unique URL."""

    url = pw.CharField(unique=True)
    added_at = pw.DateTimeField(default=datetime.now)
    encryption = pw.CharField(null=True)
    unique_size = pw.IntegerField(null=True)
    unique_csize = pw.IntegerField(null=True)
    total_size = pw.IntegerField(null=True)
    total_unique_chunks = pw.IntegerField(null=True)
    create_backup_cmd = pw.CharField(default='')
    extra_borg_arguments = pw.CharField(default='')

    def is_remote_repo(self):
        return not self.url.startswith('/')

    class Meta:
        database = DB


class RepoPassword(BaseModel):
    """Fallback to save repo passwords. Only used if no Keyring available."""

    url = pw.CharField(unique=True)
    password = pw.CharField()

    class Meta:
        database = DB


class BackupProfileModel(BaseModel):
    """Allows the user to switch between different configurations."""

    name = pw.CharField()
    added_at = pw.DateTimeField(default=datetime.now)
    repo = pw.ForeignKeyField(RepoModel, default=None, null=True)
    ssh_key = pw.CharField(default=None, null=True)
    compression = pw.CharField(default='lz4')
    exclude_patterns = pw.TextField(null=True)
    exclude_if_present = pw.TextField(null=True)
    schedule_mode = pw.CharField(default='off')
    schedule_interval_count = pw.IntegerField(default=3)
    schedule_interval_unit = pw.CharField(default='hours')
    schedule_fixed_hour = pw.IntegerField(default=3)
    schedule_fixed_minute = pw.IntegerField(default=42)
    schedule_interval_hours = pw.IntegerField(default=3)  # no longer used
    schedule_interval_minutes = pw.IntegerField(default=42)  # no longer used
    schedule_make_up_missed = pw.BooleanField(default=True)
    validation_on = pw.BooleanField(default=True)
    validation_weeks = pw.IntegerField(default=3)
    prune_on = pw.BooleanField(default=False)
    prune_hour = pw.IntegerField(default=2)
    prune_day = pw.IntegerField(default=7)
    prune_week = pw.IntegerField(default=4)
    prune_month = pw.IntegerField(default=6)
    prune_year = pw.IntegerField(default=2)
    prune_keep_within = pw.CharField(default='10H', null=True)
    new_archive_name = pw.CharField(default="{hostname}-{now:%Y-%m-%d-%H%M%S}")
    prune_prefix = pw.CharField(default="{hostname}-")
    pre_backup_cmd = pw.CharField(default='')
    post_backup_cmd = pw.CharField(default='')
    dont_run_on_metered_networks = pw.BooleanField(default=True)

    def refresh(self):
        return type(self).get(self._pk_expr())

    def slug(self):
        return slugify(self.name)

    class Meta:
        database = DB


class SourceFileModel(BaseModel):
    """A folder to be backed up, related to a Backup Configuration."""

    dir = pw.CharField()
    dir_size = pw.BigIntegerField(default=-1)
    dir_files_count = pw.BigIntegerField(default=-1)
    path_isdir = pw.BooleanField(default=False)
    profile = pw.ForeignKeyField(BackupProfileModel, default=1)
    added_at = pw.DateTimeField(default=datetime.utcnow)

    class Meta:
        database = DB
        table_name = 'sourcedirmodel'


class ArchiveModel(BaseModel):
    """An archive in a remote repository."""

    snapshot_id = pw.CharField()
    name = pw.CharField()
    repo = pw.ForeignKeyField(RepoModel, backref='archives')
    time = pw.DateTimeField()
    duration = pw.FloatField(null=True)
    size = pw.IntegerField(null=True)

    def formatted_time(self):
        return

    class Meta:
        database = DB


class WifiSettingModel(BaseModel):
    """Save Wifi Settings"""

    ssid = pw.CharField()
    last_connected = pw.DateTimeField(null=True)
    allowed = pw.BooleanField(default=True)
    profile = pw.ForeignKeyField(BackupProfileModel, default=1)

    class Meta:
        database = DB


class EventLogModel(BaseModel):
    """Keep a log of background jobs."""

    start_time = pw.DateTimeField(default=datetime.now)
    end_time = pw.DateTimeField(default=datetime.now)
    category = pw.CharField()
    subcommand = pw.CharField(null=True)
    message = pw.CharField(null=True)
    returncode = pw.IntegerField(default=-1)
    params = JSONField(null=True)
    profile = pw.CharField(null=True)
    repo_url = pw.CharField(null=True)

    class Meta:
        database = DB


class SchemaVersion(BaseModel):
    """Keep DB version to apply the correct migrations."""

    version = pw.IntegerField()
    changed_at = pw.DateTimeField(default=datetime.now)

    class Meta:
        database = DB


class SettingsModel(BaseModel):
    """App settings unrelated to a single profile or repo"""

    key = pw.CharField(unique=True)
    value = pw.BooleanField(default=False)
    str_value = pw.CharField(default='')
    label = pw.CharField()
    group = pw.CharField(default='')  # Settings group name and label
    type = pw.CharField()

    class Meta:
        database = DB


class BackupProfileMixin:
    """Extend to support multiple profiles later."""

    def profile(self):
        return BackupProfileModel.get(id=self.window().current_profile.id)
