using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
using DysonNetwork.Develop.Project;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Proto;
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using NodaTime.Serialization.Protobuf;
using NodaTime;

using VerificationMark = DysonNetwork.Shared.Data.VerificationMark;

namespace DysonNetwork.Develop.Identity;

public enum CustomAppStatus
{
    Developing,
    Staging,
    Production,
    Suspended
}

public class CustomApp : ModelBase, IIdentifiedResource
{
    public Guid Id { get; set; } = Guid.NewGuid();
    [MaxLength(1024)] public string Slug { get; set; } = null!;
    [MaxLength(1024)] public string Name { get; set; } = null!;
    [MaxLength(4096)] public string? Description { get; set; }
    public CustomAppStatus Status { get; set; } = CustomAppStatus.Developing;

    [Column(TypeName = "jsonb")] public CloudFileReferenceObject? Picture { get; set; }
    [Column(TypeName = "jsonb")] public CloudFileReferenceObject? Background { get; set; }

    [Column(TypeName = "jsonb")] public VerificationMark? Verification { get; set; }
    [Column(TypeName = "jsonb")] public CustomAppOauthConfig? OauthConfig { get; set; }
    [Column(TypeName = "jsonb")] public CustomAppLinks? Links { get; set; }

    [JsonIgnore] public ICollection<CustomAppSecret> Secrets { get; set; } = new List<CustomAppSecret>();

    public Guid ProjectId { get; set; }
    public DevProject Project { get; set; } = null!;
    
    [NotMapped]
    public Developer Developer => Project.Developer;

    [NotMapped] public string ResourceIdentifier => "custom-app:" + Id;

    public Shared.Proto.CustomApp ToProto()
    {
        return new Shared.Proto.CustomApp
        {
            Id = Id.ToString(),
            Slug = Slug,
            Name = Name,
            Description = Description ?? string.Empty,
            Status = Status switch
            {
                CustomAppStatus.Developing => Shared.Proto.CustomAppStatus.Developing,
                CustomAppStatus.Staging => Shared.Proto.CustomAppStatus.Staging,
                CustomAppStatus.Production => Shared.Proto.CustomAppStatus.Production,
                CustomAppStatus.Suspended => Shared.Proto.CustomAppStatus.Suspended,
                _ => Shared.Proto.CustomAppStatus.Unspecified
            },
            Picture = Picture?.ToProtoValue(),
            Background = Background?.ToProtoValue(),
            Verification = Verification?.ToProtoValue(),
            Links = Links is null ? null : new DysonNetwork.Shared.Proto.CustomAppLinks
            {
                HomePage = Links.HomePage ?? string.Empty,
                PrivacyPolicy = Links.PrivacyPolicy ?? string.Empty,
                TermsOfService = Links.TermsOfService ?? string.Empty
            },
            OauthConfig = OauthConfig is null ? null : new DysonNetwork.Shared.Proto.CustomAppOauthConfig
            {
                ClientUri = OauthConfig.ClientUri ?? string.Empty,
                RedirectUris = { OauthConfig.RedirectUris ?? [] },
                PostLogoutRedirectUris = { OauthConfig.PostLogoutRedirectUris ?? [] },
                AllowedScopes = { OauthConfig.AllowedScopes ?? [] },
                AllowedGrantTypes = { OauthConfig.AllowedGrantTypes ?? [] },
                RequirePkce = OauthConfig.RequirePkce,
                AllowOfflineAccess = OauthConfig.AllowOfflineAccess
            },
            ProjectId = ProjectId.ToString(),
            CreatedAt = CreatedAt.ToTimestamp(),
            UpdatedAt = UpdatedAt.ToTimestamp()
        };
    }

    public CustomApp FromProtoValue(Shared.Proto.CustomApp p)
    {
        Id = Guid.Parse(p.Id);
        Slug = p.Slug;
        Name = p.Name;
        Description = string.IsNullOrEmpty(p.Description) ? null : p.Description;
        Status = p.Status switch
        {
            Shared.Proto.CustomAppStatus.Developing => CustomAppStatus.Developing,
            Shared.Proto.CustomAppStatus.Staging => CustomAppStatus.Staging,
            Shared.Proto.CustomAppStatus.Production => CustomAppStatus.Production,
            Shared.Proto.CustomAppStatus.Suspended => CustomAppStatus.Suspended,
            _ => CustomAppStatus.Developing
        };
        ProjectId = string.IsNullOrEmpty(p.ProjectId) ? Guid.Empty : Guid.Parse(p.ProjectId);
        CreatedAt = p.CreatedAt.ToInstant();
        UpdatedAt = p.UpdatedAt.ToInstant();
        if (p.Picture is not null) Picture = CloudFileReferenceObject.FromProtoValue(p.Picture);
        if (p.Background is not null) Background = CloudFileReferenceObject.FromProtoValue(p.Background);
        if (p.Verification is not null) Verification = VerificationMark.FromProtoValue(p.Verification);
        if (p.Links is not null)
        {
            Links = new CustomAppLinks
            {
                HomePage = string.IsNullOrEmpty(p.Links.HomePage) ? null : p.Links.HomePage,
                PrivacyPolicy = string.IsNullOrEmpty(p.Links.PrivacyPolicy) ? null : p.Links.PrivacyPolicy,
                TermsOfService = string.IsNullOrEmpty(p.Links.TermsOfService) ? null : p.Links.TermsOfService
            };
        }
        return this;
    }
}

public class CustomAppLinks
{
    [MaxLength(8192)] public string? HomePage { get; set; }
    [MaxLength(8192)] public string? PrivacyPolicy { get; set; }
    [MaxLength(8192)] public string? TermsOfService { get; set; }
}

public class CustomAppOauthConfig
{
    [MaxLength(1024)] public string? ClientUri { get; set; }
    [MaxLength(4096)] public string[] RedirectUris { get; set; } = [];
    [MaxLength(4096)] public string[]? PostLogoutRedirectUris { get; set; }
    [MaxLength(256)] public string[]? AllowedScopes { get; set; } = ["openid", "profile", "email"];
    [MaxLength(256)] public string[] AllowedGrantTypes { get; set; } = ["authorization_code", "refresh_token"];
    public bool RequirePkce { get; set; } = true;
    public bool AllowOfflineAccess { get; set; } = false;
}

public class CustomAppSecret : ModelBase
{
    public Guid Id { get; set; } = Guid.NewGuid();
    [MaxLength(1024)] public string Secret { get; set; } = null!;
    [MaxLength(4096)] public string? Description { get; set; } = null!;
    public Instant? ExpiredAt { get; set; }
    public bool IsOidc { get; set; } = false; // Indicates if this secret is for OIDC/OAuth

    public Guid AppId { get; set; }
    public CustomApp App { get; set; } = null!;


    public static CustomAppSecret FromProtoValue(DysonNetwork.Shared.Proto.CustomAppSecret p)
    {
        return new CustomAppSecret
        {
            Id = Guid.Parse(p.Id),
            Secret = p.Secret,
            Description = p.Description,
            ExpiredAt = p.ExpiredAt?.ToInstant(),
            IsOidc = p.IsOidc,
            AppId = Guid.Parse(p.AppId),
        };
    }

    public DysonNetwork.Shared.Proto.CustomAppSecret ToProto()
    {
        return new DysonNetwork.Shared.Proto.CustomAppSecret
        {
            Id = Id.ToString(),
            Secret = Secret,
            Description = Description,
            ExpiredAt = ExpiredAt?.ToTimestamp(),
            IsOidc = IsOidc,
            AppId = Id.ToString(),
        };
    }
}
