# config.py -- Pamhyr configuration manager
# Copyright (C) 2023-2026  INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

# -*- coding: utf-8 -*-

import os
import pickle
import logging

from platformdirs import user_cache_dir

from SQL import SQL

from Model.Stricklers.Stricklers import Stricklers
from Model.Stricklers.StricklersList import StricklersList

from Solver.Solvers import solver_type_list

config_dir = user_cache_dir("pamhyr")
config_file = "config.sqlite3"

logger = logging.getLogger()


class Config(SQL):
    def __init__(self):
        self._version = '0.0.8'
        self.filename = Config.filename()
        self.set_default_value()

        logging.info(f"Configuration file : {self.filename}")

        super(Config, self).__init__(filename=self.filename)

    def _create(self):
        # Info (meta data)
        self.execute(
            "CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)")
        self.execute(f"INSERT INTO info VALUES ('version', '{self._version}')")

        # Key / Value (data)
        self.execute(
            "CREATE TABLE data(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)")

        # Solver
        self.execute("""
           CREATE TABLE solver(
             type TEXT NOT NULL,
             name TEXT NOT NULL UNIQUE,
             description TEXT NOT NULL,

             path_input TEXT NOT NULL,
             path_solver TEXT NOT NULL,
             path_output TEXT NOT NULL,

             cmd_input TEXT NOT NULL,
             cmd_solver TEXT NOT NULL,
             cmd_output TEXT NOT NULL
           )
           """)

        # Stricklers
        self.execute("""
           CREATE TABLE stricklers(
             name TEXT NOT NULL UNIQUE,
             comment TEXT NOT NULL,
             minor REAL NOT NULL,
             medium REAL NOT NULL
           )
           """)

        self.commit()

    def _update(self):
        version = self.execute(
            f"SELECT value FROM info WHERE key='version'")[0]

        if version != self._version:
            logger.info(
                "Configuration file update from " +
                f"{version} to {self._version}..."
            )

            major, minor, release = version.strip().split(".")

            if major == minor == "0":
                if int(release) < 2:
                    # Add default solver
                    posix = os.name == 'posix'
                    ext = "" if posix else ".exe"
                    path = os.path.join("@install_dir", "mage8", f"mage{ext}")

                    self.execute(f"""
                      INSERT INTO solver VALUES (
                        'mage8',
                        'default-mage',
                        'Default Pamhyr2 mage 8 version',

                        '', '', '',

                        '',
                        '{path} @args @input',
                        ''
                      )
                    """)

                if int(release) < 3:
                    self.execute(
                        "INSERT OR IGNORE INTO data " +
                        "VALUES ('last_study', '')"
                    )
                    self.execute(
                        "INSERT OR IGNORE INTO data " +
                        "VALUES ('close_correctly', 'True')"
                    )

                if int(release) < 4:
                    self.execute(
                        "INSERT OR IGNORE INTO data " +
                        "VALUES ('last_solver_name', '')"
                    )

                if int(release) < 5:
                    posix = os.name == 'posix'
                    ext = "" if posix else ".exe"
                    path = os.path.join("@install_dir", "mage8", f"mage{ext}")

                    self.execute(
                        "UPDATE solver SET cmd_solver=" +
                        f"'{path} @args @input' "
                        "WHERE name='default-mage'"
                    )

                if int(release) < 6:
                    posix = os.name == 'posix'
                    ext = "" if posix else ".exe"
                    path = os.path.join("@install_dir",
                                        "adists", f"adists{ext}")

                    self.execute(f"""
                      INSERT INTO solver VALUES (
                        'adistswc',
                        'default-AdisTS',
                        'Default Pamhyr2 AdisTS version',

                        '', '', '',

                        '',
                        '{path} @args @input',
                        ''
                      )
                    """)
                    self.execute(
                        "UPDATE solver SET cmd_solver=" +
                        f"'{path} @args @input' "
                        "WHERE name='default-AdisTS'"
                    )

                if int(release) < 7:
                    posix = os.name == 'posix'
                    ext = "" if posix else ".exe"
                    path = os.path.join("@install_dir",
                                        "rubar", f"rubarbe{ext}")

                    self.execute(f"""
                      INSERT INTO solver VALUES (
                        'rubar3',
                        'default-Rubar3',
                        'Default Pamhyr2 Rubar 3 version',

                        '', '', '',

                        '',
                        '{path} @args @input',
                        ''
                      )
                    """)
                    self.execute(
                        "UPDATE solver SET cmd_solver=" +
                        f"'{path} @args @input' "
                        "WHERE name='default-Rubar3'"
                    )

            if major == "0" and minor <= "2":
                if int(release) < 8:
                    posix = os.name == 'posix'
                    ext = "" if posix else ".exe"
                    path = os.path.join("@install_dir",
                                        "adists", f"adists{ext}")

                    self.execute(f"""
                      INSERT OR IGNORE INTO solver VALUES (
                        'adisttwc',
                        'default-AdisTT',
                        'Default Pamhyr2 AdisTT version',

                        '', '', '',

                        '',
                        '{path} @args @input',
                        ''
                      )
                    """)

            self.execute(
                f"UPDATE info SET value='{self._version}' " +
                "WHERE key='version'"
            )

            self.commit()

    def _load_solver(self):
        self._solvers = []

        compatible_types = ', '.join(
            map(lambda t: f"'{t}'", solver_type_list)
        )

        solvers = self.execute(
            "SELECT * FROM solver " +
            f"WHERE type IN ({compatible_types})",
            fetch_one=False
        )
        for solver in solvers:
            solver_type = solver[0]
            ctor = solver_type_list[solver_type]

            new = ctor(solver[1])
            new._description = solver[2]
            new._path_input = solver[3]
            new._path_solver = solver[4]
            new._path_output = solver[5]
            new._cmd_input = solver[6]
            new._cmd_solver = solver[7]
            new._cmd_output = solver[8]

            self._solvers.append(new)

    def _load_stricklers(self):
        self.stricklers = StricklersList()

        id = 0
        stricklers = self.execute(
            "SELECT * FROM stricklers",
            fetch_one=False
        )
        for strickler in stricklers:
            new = Stricklers()
            new._name = strickler[0]
            new._comment = strickler[1]
            new._minor = float(strickler[2])
            new._medium = float(strickler[3])

            self.stricklers.insert(id, new)
            id += 1

    def _load(self):
        # Backup
        v = self.execute("SELECT value FROM data WHERE key='backup_enable'")
        self.backup_enable = v[0] == "True"
        # v = self.execute("SELECT value FROM data WHERE key='backup_path'")
        # self.backup_path = v[0]
        v = self.execute("SELECT value FROM data WHERE key='backup_frequence'")
        self.backup_frequence = v[0]
        # v = self.execute("SELECT value FROM data WHERE key='backup_max'")
        # self.backup_max = int(v[0])

        # Editor
        v = self.execute("SELECT value FROM data WHERE key='editor'")
        self.editor = v[0]

        # Languages
        v = self.execute("SELECT value FROM data WHERE key='lang'")
        self.lang = v[0]

        # Last study
        v = self.execute("SELECT value FROM data WHERE key='last_study'")
        self.last_study = v[0]
        v = self.execute("SELECT value FROM data WHERE key='close_correctly'")
        self.close_correctly = v[0] == "True"

        # Last Solver
        v = self.execute("SELECT value FROM data WHERE key='last_solver_name'")
        self.last_solver_name = v[0]

        # Debug
        v = self.execute("SELECT value FROM data WHERE key='debug'")
        self.debug = v[0] == "True"

        self._load_solver()
        self._load_stricklers()

    def _save_solver(self):
        self.execute(f"DELETE FROM solver")

        for solver in self.solvers:
            self.execute(f"""
              INSERT INTO solver VALUES (
                '{solver._type}',
                '{self._db_format(solver._name)}',
                '{self._db_format(solver._description)}',
                '{self._db_format(solver._path_input)}',
                '{self._db_format(solver._path_solver)}',
                '{self._db_format(solver._path_output)}',
                '{self._db_format(solver._cmd_input)}',
                '{self._db_format(solver._cmd_solver)}',
                '{self._db_format(solver._cmd_output)}'
              )
              """,
                         commit=True)

    def _save_stricklers(self):
        self.execute(f"DELETE FROM stricklers")

        for stricklers in self.stricklers.stricklers:
            self.execute(f"""
              INSERT INTO stricklers VALUES (
                '{self._db_format(stricklers._name)}',
                '{self._db_format(stricklers._comment)}',
                '{stricklers._minor}',
                '{stricklers._medium}'
              )
            """)

        self.commit()

    def _save(self):
        data = {
            "backup_enable": self.backup_enable,
            # "backup_path": self.backup_path,
            "backup_frequence": self.backup_frequence,
            # "backup_max": self.backup_max,
            "editor": self.editor,
            "lang": self.lang,
            "last_study": self.last_study,
            "close_correctly": self.close_correctly,
            "debug": self.debug,
            "last_solver_name": self.last_solver_name,
        }

        for key in data:
            self.execute(
                f"INSERT OR IGNORE INTO data VALUES " +
                f" ('{key}', '{self._db_format(data[key])}')"
            )
            self.execute(
                f"UPDATE data SET " +
                f"value='{self._db_format(data[key])}' " +
                f"WHERE key='{key}'"
            )

        self.commit()
        self._save_solver()
        self._save_stricklers()

    def set_default_value(self):
        # Solvers
        self._solvers = []

        posix = os.name == 'posix'
        ext = "" if posix else ".exe"

        # Mage8
        ctor = solver_type_list["mage8"]
        new = ctor("default-mage")
        new._description = "Default Pamhyr2 mage 8 version"
        path = os.path.join("@install_dir", "mage8", "mage")
        new._cmd_solver = f""""{path}{ext}" @args @input"""
        self._solvers.append(new)

        # AdisTS
        ctor = solver_type_list["adistswc"]
        new = ctor("default-AdisTS")
        new._description = "Default Pamhyr2 AdisTS version"
        path = os.path.join("@install_dir", "adists", "adists")
        new._cmd_solver = f""""{path}{ext}" @args @input"""
        self._solvers.append(new)

        # AdisTT
        ctor = solver_type_list["adisttwc"]
        new = ctor("default-AdisTT")
        new._description = "Default Pamhyr2 AdisTT version"
        path = os.path.join("@install_dir", "adists", "adists")
        new._cmd_solver = f"\"{path}{ext}\" @args @input"
        self._solvers.append(new)

        # Rubar3
        ctor = solver_type_list["rubar3"]
        new = ctor("default-Rubar3")
        new._description = "Default Pamhyr2 Rubar 3 version"
        path = os.path.join("@install_dir", "rubar", f"rubarbe{ext}")
        new._cmd_solver = f""""{path}" @args @input"""
        self._solvers.append(new)

        # Rubar3
        ctor = solver_type_list["rubar3"]
        new = ctor("default-Rubar3")
        new._description = "Default Pamhyr2 RubarBE version"
        new._cmd_solver = f""""@install_dir/rubar/rubarbe{ext}" @args @input"""
        self._solvers.append(new)

        # Mage fake 7
#         ctor = solver_type_list["mage_fake7"]
#         new = ctor("default-mage-fake-7")
#         new._description = "Default Pamhyr2 mage fake version 7"
#         new._cmd_solver = f"""\
# "@install_dir/mage8/mage_as7{ext}" @args @input"""
#         self._solvers.append(new)

        # Backup
        self.backup_enable = True
        # self.backup_path = ""
        self.backup_frequence = "00:15:00"
        # self.backup_max = 10

        # Editor
        self.editor = "editor @file"

        # Languages
        self.lang = ""

        # Stricklers
        self.stricklers = StricklersList()

        # Last study
        self.last_study = ""
        self.close_correctly = False

        # Last Solver
        self.last_solver_name = ""

        # Debug
        self.debug = False

    def set_close_correctly(self):
        self.close_correctly = True
        self.execute(
            f"UPDATE data SET value='True' WHERE key='close_correctly'")
        self.commit()

    def set_last_study(self, filename):
        if filename is None:
            return

        self.last_study = filename
        self.close_correctly = False
        self.execute(
            "UPDATE data SET " +
            f"value='{self._db_format(self.last_study)}' " +
            "WHERE key='last_study'"
        )
        self.execute(
            f"UPDATE data SET value='{self.close_correctly}' " +
            "WHERE key='close_correctly'"
        )
        self.commit()

    def update_last_solver_used(self, solver_name):
        self.last_solver_name = solver_name
        self.execute(
            "UPDATE data SET " +
            f"value='{self._db_format(self.last_solver_name)}' " +
            "WHERE key='last_solver_name'"
        )

    @classmethod
    def filename(cls):
        file = ""

        if os.name == 'posix':
            ndir = config_dir
        else:
            ndir = config_dir
            ndir = ndir.replace("/", "\\")

        os.makedirs(ndir, exist_ok=True)
        file = os.path.join(ndir, config_file)

        return file

    @classmethod
    def languages(cls):
        return {
            "System":  "",
            "English": "en",
            "French":  "fr",
        }

    @property
    def solvers(self):
        return self._solvers.copy()

    @solvers.setter
    def solvers(self, solvers):
        self._solvers = solvers

    def save(self):
        self._save()

    @classmethod
    def load(cls):
        return cls()
