using OF_DL.Models.Config;

namespace OF_DL.Gui.Services;

internal static class ConfigValidationService
{
    public static IReadOnlyDictionary<string, string> Validate(Config config)
    {
        Dictionary<string, string> errors = new(StringComparer.Ordinal);

        ValidatePath(config.DownloadPath, nameof(Config.DownloadPath), errors, requireExistingFile: false);
        ValidatePath(config.FFmpegPath, nameof(Config.FFmpegPath), errors, requireExistingFile: true);
        ValidatePath(config.FFprobePath, nameof(Config.FFprobePath), errors, requireExistingFile: true);

        if (config.Timeout.HasValue && config.Timeout.Value <= 0 && config.Timeout.Value != -1)
        {
            errors[nameof(Config.Timeout)] = "Timeout must be -1 or greater than 0.";
        }

        if (config.LimitDownloadRate && config.DownloadLimitInMbPerSec <= 0)
        {
            errors[nameof(Config.DownloadLimitInMbPerSec)] =
                "DownloadLimitInMbPerSec must be greater than 0 when LimitDownloadRate is enabled.";
        }

        if (config.DownloadOnlySpecificDates && !config.CustomDate.HasValue)
        {
            errors[nameof(Config.CustomDate)] = "CustomDate is required when DownloadOnlySpecificDates is enabled.";
        }

        ValidateFileNameFormat(config.PaidPostFileNameFormat, nameof(Config.PaidPostFileNameFormat), errors);
        ValidateFileNameFormat(config.PostFileNameFormat, nameof(Config.PostFileNameFormat), errors);
        ValidateFileNameFormat(config.PaidMessageFileNameFormat, nameof(Config.PaidMessageFileNameFormat), errors);
        ValidateFileNameFormat(config.MessageFileNameFormat, nameof(Config.MessageFileNameFormat), errors);

        foreach (KeyValuePair<string, CreatorConfig> creatorConfig in config.CreatorConfigs)
        {
            ValidateFileNameFormat(creatorConfig.Value.PaidPostFileNameFormat,
                $"{nameof(Config.CreatorConfigs)}.{creatorConfig.Key}.PaidPostFileNameFormat", errors);
            ValidateFileNameFormat(creatorConfig.Value.PostFileNameFormat,
                $"{nameof(Config.CreatorConfigs)}.{creatorConfig.Key}.PostFileNameFormat", errors);
            ValidateFileNameFormat(creatorConfig.Value.PaidMessageFileNameFormat,
                $"{nameof(Config.CreatorConfigs)}.{creatorConfig.Key}.PaidMessageFileNameFormat", errors);
            ValidateFileNameFormat(creatorConfig.Value.MessageFileNameFormat,
                $"{nameof(Config.CreatorConfigs)}.{creatorConfig.Key}.MessageFileNameFormat", errors);
        }

        return errors;
    }

    private static void ValidatePath(string? path, string fieldName, IDictionary<string, string> errors,
        bool requireExistingFile)
    {
        if (string.IsNullOrWhiteSpace(path))
        {
            return;
        }

        if (path.Any(character => Path.GetInvalidPathChars().Contains(character)))
        {
            errors[fieldName] = "Path contains invalid characters.";
            return;
        }

        if (requireExistingFile && !File.Exists(path))
        {
            errors[fieldName] = "Path must point to an existing file.";
        }
    }

    private static void ValidateFileNameFormat(string? format, string fieldName, IDictionary<string, string> errors)
    {
        if (string.IsNullOrWhiteSpace(format))
        {
            return;
        }

        bool hasUniqueToken = format.Contains("{mediaId}", StringComparison.OrdinalIgnoreCase) ||
                              format.Contains("{filename}", StringComparison.OrdinalIgnoreCase);
        if (!hasUniqueToken)
        {
            errors[fieldName] = "Format must include {mediaId} or {filename} to avoid file collisions.";
        }
    }
}
