using Npgsql;

namespace TravelAgencyWinForms.Utils;

public static class ErrorHelper
{
    private static readonly Dictionary<string, string> TableNames = new(StringComparer.OrdinalIgnoreCase)
    {
        ["clients"] = "клиентах",
        ["employees"] = "сотрудниках",
        ["positions"] = "должностях",
        ["cities"] = "городах",
        ["tours"] = "турах",
        ["tour_cities"] = "маршрутах туров",
        ["tour_list"] = "расписании туров",
        ["bookings"] = "бронированиях",
        ["app_users"] = "пользователях",
        ["audit_log"] = "журнале операций"
    };

    public static string ToUserMessage(Exception exception)
    {
        if (exception is PostgresException postgres)
            return TranslatePostgres(postgres);

        var message = exception.Message;

        if (message.Contains("23503") || message.Contains("referenced from table", StringComparison.OrdinalIgnoreCase))
            return "Запись нельзя удалить, потому что она используется в связанных данных.";

        if (message.Contains("duplicate key", StringComparison.OrdinalIgnoreCase))
            return "Запись с такими уникальными данными уже существует.";

        return message;
    }

    private static string TranslatePostgres(PostgresException error)
    {
        var constraint = error.ConstraintName ?? string.Empty;
        var detail = error.Detail ?? string.Empty;

        if (error.SqlState == "23503")
            return ForeignKeyMessage(error, detail);

        if (error.SqlState == "23505")
            return DuplicateMessage(constraint);

        if (error.SqlState == "23502")
            return "Заполните обязательные поля.";

        if (error.SqlState == "23514")
            return CheckMessage(constraint);

        return string.IsNullOrWhiteSpace(error.MessageText)
            ? "Не удалось выполнить операцию. Проверьте данные и попробуйте ещё раз."
            : error.MessageText;
    }

    private static string ForeignKeyMessage(PostgresException error, string detail)
    {
        var relatedTable = ExtractRelatedTable(detail);
        if (!string.IsNullOrWhiteSpace(relatedTable) && TableNames.TryGetValue(relatedTable, out var tableLabel))
            return $"Запись нельзя удалить, потому что она используется в {tableLabel}.";

        if (!string.IsNullOrWhiteSpace(error.TableName) && TableNames.TryGetValue(error.TableName, out var currentTableLabel))
            return $"Операцию нельзя выполнить: запись связана с данными в разделе «{currentTableLabel}».";

        return "Запись нельзя удалить, потому что она используется в связанных данных.";
    }

    private static string ExtractRelatedTable(string detail)
    {
        var marker = "referenced from table";
        var index = detail.IndexOf(marker, StringComparison.OrdinalIgnoreCase);
        if (index < 0)
            return string.Empty;

        var tail = detail[(index + marker.Length)..].Trim();

        if (tail.StartsWith('"'))
        {
            var end = tail.IndexOf('"', 1);
            if (end > 1)
                return tail[1..end];
        }

        return tail.Split(' ', '.', ',', ';').FirstOrDefault() ?? string.Empty;
    }

    private static string DuplicateMessage(string constraint)
    {
        if (constraint.Contains("passport", StringComparison.OrdinalIgnoreCase))
            return "Запись с такими паспортными данными уже существует.";

        if (constraint.Contains("booking_number", StringComparison.OrdinalIgnoreCase))
            return "Бронирование с таким номером уже существует.";

        if (constraint.Contains("login", StringComparison.OrdinalIgnoreCase))
            return "Пользователь с таким логином уже существует.";

        if (constraint.Contains("city", StringComparison.OrdinalIgnoreCase))
            return "Такой город уже есть в справочнике.";

        if (constraint.Contains("position", StringComparison.OrdinalIgnoreCase))
            return "Такая должность уже есть в справочнике.";

        return "Запись с такими уникальными данными уже существует.";
    }

    private static string CheckMessage(string constraint)
    {
        if (constraint.Contains("phone", StringComparison.OrdinalIgnoreCase))
            return "Телефон должен содержать 10–12 цифр и может начинаться с +.";

        if (constraint.Contains("email", StringComparison.OrdinalIgnoreCase))
            return "Введите корректный адрес электронной почты.";

        if (constraint.Contains("passport", StringComparison.OrdinalIgnoreCase))
            return "Паспортные данные должны быть в формате: 0000 000000.";

        if (constraint.Contains("salary", StringComparison.OrdinalIgnoreCase))
            return "Зарплата не может быть отрицательной.";

        if (constraint.Contains("price", StringComparison.OrdinalIgnoreCase))
            return "Стоимость тура не может быть отрицательной.";

        if (constraint.Contains("birth", StringComparison.OrdinalIgnoreCase))
            return "Дата рождения указана некорректно.";

        if (constraint.Contains("tour_list_dates", StringComparison.OrdinalIgnoreCase))
            return "Дата окончания тура не может быть раньше даты начала.";

        if (constraint.Contains("status", StringComparison.OrdinalIgnoreCase))
            return "Выбран недопустимый статус бронирования.";

        return "Проверьте корректность заполненных данных.";
    }
}
