# Copyright (C) 2020, 2021 The Meme Factory, Inc.  http://www.karlpinc.com/

# This file is part of PGWUI_Bulk_Upload.
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero 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
# Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License along with this program.  If not, see
# <http://www.gnu.org/licenses/>.
#

# Karl O. Pinc <kop@karlpinc.com>

from pgwui_core import exceptions as core_ex
# PGWUI setting related exceptions
from pgwui_core.exceptions import SetupError


# Line-by-line processing errors

class CannotReReadError(core_ex.DataLineError):
    def __init__(self, filename, lineno, exp):
        super().__init__(
            lineno,
            f'Cannot re-open ({filename}) to read db data',
            f'The error is: {exp}')


# Exceptions that require detail

class NotAZipfileError(SetupError):
    def __init__(self, filename):
        super().__init__(
            'No zip file uploaded',
            f'The uploaded file ({filename}) is not recognized as a zip file')


class CannotUnarchiveError(SetupError):
    def __init__(self, filename, err):
        super().__init__(
            'Cannot unzip',
            (f'The uploaded file ({filename}) cannot be unzipped, '
             'the error reported is: err'))


class NotADirectoryError(SetupError):
    def __init__(self, filename, name):
        super().__init__(
            'Not a directory',
            f'The uploaded file ({filename}) has a top-level '
            f'entry ({name}) that is not a directory',
            'The entries at the top of the zip file must be directories')


class EmptyArchiveError(SetupError):
    def __init__(self, filename, err):
        super().__init__(
            'Empty zip file',
            f'The uploaded file ({filename}) has no content')


class NotAFileError(SetupError):
    def __init__(self, filename, name):
        super().__init__(
            'Not a file',
            f'The uploaded file ({filename}) has a 2nd-level '
            f'entry ({name}) that is not a file',
            'The directories in the zip file may only contain files')


class NoMapfileError(SetupError):
    def __init__(self, filename, map_file):
        super().__init__(
            'Missing map file',
            f'The map file ({map_file}) not found in the '
            f'uploaded file ({filename})',
            'The directories in the zip file must contain a file which '
            'maps file names to table names')


class CannotReadError(SetupError):
    def __init__(self, filename, exp):
        super().__init__(
            f'Cannot open ({filename}) for reading',
            f'The error is: {exp}')


class CannotRollbackError(SetupError):
    def __init__(self, exp):
        super().__init__(
            'Cannot roll back the current transaction',
            'The transaction has failed and must roll back before '
            'the database can again be queried about relations and '
            'their columns',
            f'The error from psycopg2 is: ({exp})')


class BadMapFileError(SetupError):
    pass


class BadMapfileYAMLError(BadMapFileError):
    def __init__(self, filename, map_file, exp):
        super().__init__(
            'Bad YAML in map file',
            f'The map file ({map_file}) in the '
            f'uploaded file ({filename}) cannot be parsed as YAML: {exp}',
            'The directories in the zip file must contain a map file in YAML '
            'format to maps file names to table names')


class NoMapListError(BadMapFileError):
    def __init__(self, filename):
        super().__init__(
            f'Missing "map_list" tag in map file ({filename})',
            'The map file does not have "map_list" as a top-level tag')


class BadMapListError(BadMapFileError):
    def __init__(self, filename):
        super().__init__(
            f'The "map_list" tag in map file ({filename}) is not a list',
            'The map file does not have a list in the "map_list" tag')


class BadMapListEntryError(BadMapFileError):
    def __init__(self, filename, item):
        super().__init__(
            f'The "map_list" tag of map file ({filename}) contains an entry '
            'which is not a map',
            f'The list item ({item}) is not a map')


class MustBeStringError(BadMapFileError):
    def __init__(self, filename, count, key, value):
        super().__init__(
            f'The "map_list" tag of map file ({filename}) contains a map '
            'with an invalid value',
            f'The value ({value}) of key ({key}) of map_list item number '
            f'{count} is not a string')


class MissingMapListTagError(BadMapFileError):
    def __init__(self, filename, count, key):
        super().__init__(
            f'The "map_list" tag of map file ({filename}) contains a map '
            'missing a required tag',
            f'The tag ({key}) is missing from map_list item number {count}')


class ExtraMapListTagError(BadMapFileError):
    def __init__(self, filename, count, key):
        super().__init__(
            f'The "map_list" tag of map file ({filename}) contains a map '
            'with an unknown tag',
            f'The tag ({key}) is not recognized in map_list item '
            f'number {count}')


class BadTrimValueError(BadMapFileError):
    def __init__(self, filename, count, value):
        super().__init__(
            f'The "map_list" tag of map file ({filename}) contains a map '
            'with a file_map value that has a non-boolean trim value',
            f'The trim value must be a YAML boolean, ({value}) was supplied '
            f'for trim in map_list item number {count}')


class MissingFileMapTagError(BadMapFileError):
    def __init__(self, filename, count, key):
        super().__init__(
            f'The "map_list" tag of map file ({filename}) contains a '
            'file_map map missing a required tag',
            f'The tag ({key}) is missing from map_list item number {count}')


class ExtraFileMapTagError(BadMapFileError):
    def __init__(self, filename, count, key):
        super().__init__(
            f'The "map_list" tag of map file ({filename}) contains a '
            'file_map map with an unknown tag',
            f'The tag ({key}) is not recognized in map_list item '
            f'number {count}')


class DuplicateMappingError(BadMapFileError):
    def __init__(self, filename, pos0, pos1, file, relation):
        super().__init__(
            f'The "map_list" tag of map file ({filename}) specifies duplicate '
            'uploads',
            f'The list entries {pos0} and {pos1} both upload file ({file}) '
            f'into relation ({relation})')


class MissingFileError(BadMapFileError):
    def __init__(self, filename, item):
        super().__init__(
            f'The "map_list" tag of map file ({filename}) references a '
            "file not in the zip file's directory",
            f'Missing file ({item})')


class ExtraFileError(BadMapFileError):
    def __init__(self, filename, extrafile):
        super().__init__(
            f'The "map_list" tag of the map file ({filename}) does not '
            'contain an entry for a file in the zip file',
            f'Extra file ({extrafile})')
