using Tiku.Api.Middleware;
using Tiku.Application.Assets;
using Tiku.Application.Content;
using Tiku.Application.Profile;
using Tiku.Application.QuestionBanks;
using Tiku.Application.Scoreline;

namespace Tiku.Api.Modules.Student.Content.Errors;

internal sealed class ContentExceptionProblemDetailsMapper : ExceptionProblemDetailsMapper
{
    public override IReadOnlyCollection<Type> HandledExceptionTypes { get; } =
    [
        typeof(PublicQuestionAccessDeniedException), typeof(QuestionLocatorException), typeof(RequiredFieldException),
        typeof(ContentNavigationNotFoundException), typeof(QuestionBankRequiredFieldException),
        typeof(QuestionBankNotFoundException), typeof(AssetAccessException), typeof(AssetManagementException),
        typeof(VideoPlaybackException), typeof(ContentManagementException), typeof(ScorelineQueryException),
        typeof(ProfileException)
    ];

    public override bool TryMap(Exception exception, out ExceptionProblemDetailsMapping mapping)
    {
        return exception switch
        {
            PublicQuestionAccessDeniedException denied => Mapped(denied, StatusCodes.Status403Forbidden, denied.Code,
                out mapping),
            QuestionLocatorException locator => Mapped(locator, StatusCodes.Status404NotFound, locator.Code,
                out mapping),
            RequiredFieldException => Mapped(exception, StatusCodes.Status400BadRequest, "required_field", out mapping),
            ContentNavigationNotFoundException => Mapped(exception, StatusCodes.Status404NotFound,
                "content_navigation_not_found", out mapping),
            QuestionBankRequiredFieldException => Mapped(exception, StatusCodes.Status400BadRequest, "required_field",
                out mapping),
            QuestionBankNotFoundException => Mapped(exception, StatusCodes.Status404NotFound, "question_not_found",
                out mapping),
            AssetAccessException asset => Mapped(asset, AssetAccessStatus(asset.Code), asset.Code.ToLowerInvariant(),
                out mapping),
            AssetManagementException asset => Mapped(asset, AssetManagementStatus(asset.Code), asset.Code, out mapping),
            VideoPlaybackException video => Mapped(video, VideoStatus(video.Code), video.Code, out mapping),
            ContentManagementException content => Mapped(content, ContentStatus(content.Code), content.Code, out mapping),
            ScorelineQueryException scoreline => Mapped(scoreline, StatusCodes.Status400BadRequest, scoreline.Code,
                out mapping),
            ProfileException profile => Mapped(profile, ProfileStatus(profile.Code), profile.Code, out mapping),
            _ => NotMapped(out mapping)
        };
    }

    private static int AssetAccessStatus(string code) => code switch
    {
        "ASSET_NOT_FOUND" => StatusCodes.Status404NotFound,
        "AUTH_REQUIRED" => StatusCodes.Status401Unauthorized,
        "ASSET_HIDDEN" or "ASSET_MEMBERSHIP_REQUIRED" or "ASSET_SVIP_REQUIRED" => StatusCodes.Status403Forbidden,
        "ASSET_UPLOAD_NOT_VERIFIED" or "ASSET_SECURITY_SCAN_NOT_PASSED" => StatusCodes.Status409Conflict,
        _ => StatusCodes.Status400BadRequest
    };

    private static int AssetManagementStatus(string code) => code switch
    {
        "asset_not_found" or "import_job_not_found" => StatusCodes.Status404NotFound,
        "asset_upload_missing" => StatusCodes.Status409Conflict,
        "tenant_content_access_denied" => StatusCodes.Status403Forbidden,
        _ => StatusCodes.Status400BadRequest
    };

    private static int VideoStatus(string code) => code switch
    {
        "video_access_denied" => StatusCodes.Status403Forbidden,
        "video_not_found" or "question_video_not_found" => StatusCodes.Status404NotFound,
        _ => StatusCodes.Status400BadRequest
    };

    private static int ContentStatus(string code) => code switch
    {
        "entry_not_found" or "node_not_found" or "collection_not_found" or "question_not_found" or
            "import_type_invalid" => StatusCodes.Status404NotFound,
        "tenant_content_access_denied" => StatusCodes.Status403Forbidden,
        _ when code.EndsWith("_not_found", StringComparison.Ordinal) => StatusCodes.Status404NotFound,
        _ => StatusCodes.Status400BadRequest
    };

    private static int ProfileStatus(string code) => code switch
    {
        "profile_access_denied" => StatusCodes.Status403Forbidden,
        "profile_user_not_found" or "check_in_task_not_found" or "region_not_found" or "school_not_found" or
            "major_not_found" => StatusCodes.Status404NotFound,
        _ => StatusCodes.Status400BadRequest
    };
}
