//
// Copyright © 2024 MediaMall Technologies, Inc.  All rights reserved.
//

#import "YCConnect+Additions.h"
#import <React/RCTUtils.h>
#include <objc/runtime.h>
#include <stdatomic.h>

#define E2S(t,a) @(t##a) : @""#a
#define S2E(t,a) @""#a : @(t##a)

@implementation YCSRErrorEnum : NSObject

+ (NSString *)toString:(YCSError)value
{
    static dispatch_once_t once;
    static NSDictionary<NSNumber *, NSString *> *map = nil;
    dispatch_once(&once, ^
    {
        map = @
        {
            E2S(YCSError, Unknown),
            E2S(YCSError, SDKVersionNotSupported),
            E2S(YCSError, SystemMaintenance),
            E2S(YCSError, NetworkRequestFailed),
            E2S(YCSError, OperationCanceled),
            E2S(YCSError, InvalidArgument),
            E2S(YCSError, ServiceFetchingFailed),
            E2S(YCSError, ServiceLinkingFailed),
            E2S(YCSError, ServiceUnsupported),
            E2S(YCSError, ServiceTemporarilyUnavailable),
            E2S(YCSError, ServiceLoginFailed),
            E2S(YCSError, ServiceProfileNotFound),
            E2S(YCSError, ServiceProfilePinMismatch),
            E2S(YCSError, ServiceContentNotFound),
            E2S(YCSError, InvalidViewContext),
            E2S(YCSError, UserConsentRequired)
        };
    });

    NSString *res = [map objectForKey:@(value)];
    return (res != nil) ? res : [map objectForKey:@(YCSErrorUnknown)];
}

@end


@implementation NSError (YCSRNSErrorAdditions)

- (NSString *)codeString
{
    return [NSString stringWithFormat:@"%ld", (long)self.code];
}

- (NSDictionary *)JSONDictionary
{
    return RCTJSErrorFromCodeMessageAndNSError(self.codeString, self.localizedDescription, self);
}

@end


@implementation YCSRLogLevelEnum : NSObject

+ (NSString *)toString:(YCSLogLevel)value
{
    static dispatch_once_t once;
    static NSDictionary<NSNumber *, NSString *> *map = nil;
    dispatch_once(&once, ^
    {
        map = @
        {
            E2S(YCSLogLevel, Trace),
            E2S(YCSLogLevel, Debug),
            E2S(YCSLogLevel, Information),
            E2S(YCSLogLevel, Warning),
            E2S(YCSLogLevel, Error),
            E2S(YCSLogLevel, Critical),
            E2S(YCSLogLevel, None)
        };
    });

    NSString *res = [map objectForKey:@(value)];
    return (res != nil) ? res : [map objectForKey:@(YCSLogLevelNone)];
}

+ (YCSLogLevel)fromString:(NSString *)value
{
    static dispatch_once_t once;
    static NSDictionary<NSString *, NSNumber *> *map = nil;
    dispatch_once(&once, ^
    {
        map = @
        {
            S2E(YCSLogLevel, Trace),
            S2E(YCSLogLevel, Debug),
            S2E(YCSLogLevel, Information),
            S2E(YCSLogLevel, Warning),
            S2E(YCSLogLevel, Error),
            S2E(YCSLogLevel, Critical),
            S2E(YCSLogLevel, None)
        };
    });

    NSNumber *res = [map objectForKey:value];
    return (res != nil) ? [res intValue] : YCSLogLevelNone;
}

+ (YCSLogLevel)fromObject:(id)value
{
    if ([value isKindOfClass:[NSString class]])
    {
        NSString *logLevelString = value;
        return [YCSRLogLevelEnum fromString:logLevelString];
    }
    else if ([value isKindOfClass:[NSNumber class]])
    {
        NSNumber *logLevelNumber = value;
        return [logLevelNumber intValue];
    }

    return YCSLogLevelNone;
}

@end


@implementation YCSConnectOptions (YCSRConnectOptionsAdditions)

- (instancetype)initWithJSONDictionary:(NSDictionary *)dict
{
    if (self = [super init])
    {
        self.key = [dict objectForKey:@"key"];
        self.logLevel = [YCSRLogLevelEnum fromObject:[dict objectForKey:@"logLevel"]];
        self.accessToken = [dict objectForKey:@"accessToken"];
        self.refreshToken = [dict objectForKey:@"refreshToken"];
        self.extra = [dict objectForKey:@"extra"];
    }

    return self;
}

@end


@implementation YCSStreamingServiceLink (YCSRStreamingServiceLinkAdditions)

- (instancetype)initWithJSONDictionary:(NSDictionary *)dict
{
    if (self = [super init])
    {
        self.username = [dict objectForKey:@"username"];
        self.profileName = [dict objectForKey:@"profileName"];
        self.profileImageUrl = [dict objectForKey:@"profileImageUrl"];
        self.isProfilePINProtected = [(NSNumber *)[dict objectForKey:@"isProfilePINProtected"] boolValue];
        self.isBroken = [(NSNumber *)[dict objectForKey:@"isBroken"] boolValue];
    }

    return self;
}

- (NSMutableDictionary *)JSONDictionary
{
    NSUInteger const capacity = 5;
    NSMutableArray<id> *objects = [[NSMutableArray<id> alloc] initWithCapacity:capacity];
    NSMutableArray<NSString *> *keys = [[NSMutableArray<NSString *> alloc] initWithCapacity:capacity];

    [objects addObject:self.username ? self.username : [NSNull null]];
    [keys addObject:@"username"];

    if (self.profileName)
    {
        [objects addObject:self.profileName];
        [keys addObject:@"profileName"];
    }

    if (self.profileImageUrl)
    {
        [objects addObject:self.profileImageUrl];
        [keys addObject:@"profileImageUrl"];
    }

    [objects addObject:@(self.isProfilePINProtected)];
    [keys addObject:@"isProfilePINProtected"];

    [objects addObject:@(self.isBroken)];
    [keys addObject:@"isBroken"];

    return [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
}

@end


@implementation YCSStreamingService (YCSRStreamingServiceAdditions)

- (instancetype)initWithJSONDictionary:(NSDictionary *)dict
{
    if (self = [super init])
    {
        self.name = [dict objectForKey:@"name"];
        self.smallThumbnailUrl = [dict objectForKey:@"smallThumbnailUrl"];
        self.largeThumbnailUrl = [dict objectForKey:@"largeThumbnailUrl"];
        self.largeThumbnailUrl = [dict objectForKey:@"overlayThumbnailUrl"];
        self.isAvailable = [(NSNumber *)[dict objectForKey:@"isAvailable"] boolValue];

        NSDictionary *link = [dict objectForKey:@"link"];
        if ((link != nil) && ![link isKindOfClass:[NSNull class]])
            self.link = [[YCSStreamingServiceLink alloc] initWithJSONDictionary:link];
    }

    return self;
}

- (NSMutableDictionary *)JSONDictionary
{
    NSUInteger const capacity = 6;
    NSMutableArray<id> *objects = [[NSMutableArray<id> alloc] initWithCapacity:capacity];
    NSMutableArray<NSString *> *keys = [[NSMutableArray<NSString *> alloc] initWithCapacity:capacity];

    [objects addObject:self.name];
    [keys addObject:@"name"];

    if (self.smallThumbnailUrl)
    {
        [objects addObject:self.smallThumbnailUrl];
        [keys addObject:@"smallThumbnailUrl"];
    }

    if (self.largeThumbnailUrl)
    {
        [objects addObject:self.largeThumbnailUrl];
        [keys addObject:@"largeThumbnailUrl"];
    }

    if (self.overlayThumbnailUrl)
    {
        [objects addObject:self.overlayThumbnailUrl];
        [keys addObject:@"overlayThumbnailUrl"];
    }

    [objects addObject:@(self.isAvailable)];
    [keys addObject:@"isAvailable"];

    if (self.link)
    {
        [objects addObject:[self.link JSONDictionary]];
        [keys addObject:@"link"];
    }

    return [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
}

@end


@implementation YCSRStreamingContentTypeEnum

+ (NSString *)toString:(YCSStreamingContentType)value
{
    static dispatch_once_t once;
    static NSDictionary<NSNumber *, NSString *> *map = nil;
    dispatch_once(&once, ^
    {
        map = @
        {
            E2S(YCSStreamingContentType, Invalid),
            E2S(YCSStreamingContentType, Folder),
            E2S(YCSStreamingContentType, Video)
        };
    });

    NSString *res = [map objectForKey:@(value)];
    return (res != nil) ? res : [map objectForKey:@(YCSStreamingContentTypeInvalid)];
}

+ (YCSStreamingContentType)fromString:(NSString *)value
{
    static dispatch_once_t once;
    static NSDictionary<NSString *, NSNumber *> *map = nil;
    dispatch_once(&once, ^
    {
        map = @
        {
            S2E(YCSStreamingContentType, Invalid),
            S2E(YCSStreamingContentType, Folder),
            S2E(YCSStreamingContentType, Video)
        };
    });

    NSNumber *res = [map objectForKey:value];
    return (res != nil) ? [res intValue] : YCSStreamingContentTypeInvalid;
}

+ (YCSStreamingContentType)fromObject:(id)value
{
    if ([value isKindOfClass:[NSString class]])
    {
        NSString *contentTypeString = value;
        return [YCSRStreamingContentTypeEnum fromString:contentTypeString];
    }
    else if ([value isKindOfClass:[NSNumber class]])
    {
        NSNumber *contentTypeNumber = value;
        return [contentTypeNumber intValue];
    }

    return YCSStreamingContentTypeInvalid;
}

@end


@implementation YCSStreamingContent (YCSRStreamingContentAdditions)

- (instancetype)initWithJSONDictionary:(NSDictionary *)dict
{
    if (self = [super init])
    {
        self.type = [YCSRStreamingContentTypeEnum fromObject:[dict objectForKey:@"type"]];
        self.name = [dict objectForKey:@"name"];
        self.title = [dict objectForKey:@"title"];
        self.overview = [dict objectForKey:@"overview"];
        self.duration = [(NSNumber *)[dict objectForKey:@"duration"] longLongValue];

        long long us = [(NSNumber *)[dict objectForKey:@"airDate"] longLongValue];
        if (us > 0)
        {
            NSTimeInterval s = us / 1000.0;
            self.airDate = [NSDate dateWithTimeIntervalSince1970:s];
        }

        self.smallThumbnailUrl = [dict objectForKey:@"smallThumbnailUrl"];
        self.largeThumbnailUrl = [dict objectForKey:@"largeThumbnailUrl"];

        self.series = [dict objectForKey:@"series"];
        self.season = [dict objectForKey:@"season"];
        self.episode = [dict objectForKey:@"episode"];
        self.releaseYear = [dict objectForKey:@"releaseYear"];
        self.contentRating = [dict objectForKey:@"contentRating"];
        self.watchNowUrl = [dict objectForKey:@"watchNowUrl"];

        self.tmdbID = [dict objectForKey:@"tmdbID"];
        self.tmdbSeriesID = [dict objectForKey:@"tmdbSeriesID"];

        self.path = [dict objectForKey:@"path"];
    }

    return self;
}

- (NSMutableDictionary *)JSONDictionary
{
    NSUInteger const capacity = 17;
    NSMutableArray<id> *objects = [[NSMutableArray<id> alloc] initWithCapacity:capacity];
    NSMutableArray<NSString *> *keys = [[NSMutableArray<NSString *> alloc] initWithCapacity:capacity];

    [objects addObject:[YCSRStreamingContentTypeEnum toString:self.type]];
    [keys addObject:@"type"];

    [objects addObject:self.name ? self.name : [NSNull null]];
    [keys addObject:@"name"];

    [objects addObject:self.title ? self.title : [NSNull null]];
    [keys addObject:@"title"];

    if (self.overview)
    {
        [objects addObject:self.overview];
        [keys addObject:@"overview"];
    }

    [objects addObject:[NSNumber numberWithLongLong:self.duration]];
    [keys addObject:@"duration"];

    if (self.airDate)
    {
        [objects addObject:[NSNumber numberWithLongLong:(long long)(self.airDate.timeIntervalSince1970 * 1000)]];
        [keys addObject:@"airDate"];
    }

    if (self.smallThumbnailUrl)
    {
        [objects addObject:self.smallThumbnailUrl];
        [keys addObject:@"smallThumbnailUrl"];
    }

    if (self.largeThumbnailUrl)
    {
        [objects addObject:self.largeThumbnailUrl];
        [keys addObject:@"largeThumbnailUrl"];
    }

    if (self.series)
    {
        [objects addObject:self.series];
        [keys addObject:@"series"];
    }

    if (self.season)
    {
        [objects addObject:self.season];
        [keys addObject:@"season"];
    }

    if (self.episode)
    {
        [objects addObject:self.episode];
        [keys addObject:@"episode"];
    }

    if (self.releaseYear)
    {
        [objects addObject:self.releaseYear];
        [keys addObject:@"releaseYear"];
    }

    if (self.contentRating)
    {
        [objects addObject:self.contentRating];
        [keys addObject:@"contentRating"];
    }

    if (self.watchNowUrl)
    {
        [objects addObject:self.watchNowUrl];
        [keys addObject:@"watchNowUrl"];
    }

    if (self.tmdbID)
    {
        [objects addObject:self.tmdbID];
        [keys addObject:@"tmdbID"];
    }

    if (self.tmdbSeriesID)
    {
        [objects addObject:self.tmdbSeriesID];
        [keys addObject:@"tmdbSeriesID"];
    }

    if (self.path)
    {
        [objects addObject:self.path];
        [keys addObject:@"path"];
    }

    return [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
}

@end


@implementation YCSStreamingContentDetails (YCSRStreamingContentDetailsAdditions)

- (NSMutableDictionary *)JSONDictionary
{
    NSUInteger const capacity = 1;
    NSMutableArray<id> *objects = [[NSMutableArray<id> alloc] initWithCapacity:capacity];
    NSMutableArray<NSString *> *keys = [[NSMutableArray<NSString *> alloc] initWithCapacity:capacity];

    [objects addObject:[NSNumber numberWithLongLong:self.estimatedDuration]];
    [keys addObject:@"estimatedDuration"];

    return [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
}

@end


@implementation YCSStreamingCategory (YCSRStreamingCategoryAdditions)

- (instancetype)initWithJSONDictionary:(NSDictionary *)dict
{
    if (self = [super init])
        self.name = [dict objectForKey:@"name"];

    return self;
}

- (NSMutableDictionary *)JSONDictionary
{
    NSUInteger const capacity = 1;
    NSMutableArray<id> *objects = [[NSMutableArray<id> alloc] initWithCapacity:capacity];
    NSMutableArray<NSString *> *keys = [[NSMutableArray<NSString *> alloc] initWithCapacity:capacity];

    [objects addObject:self.name];
    [keys addObject:@"name"];

    return [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
}

@end


@implementation YCSFetchContentCategoryServiceResult (YCSRFetchContentCategoryServiceResultAdditions)

- (NSMutableDictionary *)JSONDictionary
{
    NSUInteger const capacity = 2;
    NSMutableArray<id> *objects = [[NSMutableArray<id> alloc] initWithCapacity:capacity];
    NSMutableArray<NSString *> *keys = [[NSMutableArray<NSString *> alloc] initWithCapacity:capacity];

    [objects addObject:[self.service JSONDictionary]];
    [keys addObject:@"service"];

    NSMutableArray<NSDictionary *> *streamingContent = [[NSMutableArray<NSDictionary *> alloc] initWithCapacity:self.content.count];
    for (YCSStreamingContent *content in self.content)
        [streamingContent addObject:[content JSONDictionary]];

    [objects addObject:streamingContent];
    [keys addObject:@"content"];

    return [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
}

@end


@implementation YCSFetchContentCategoryResult (YCSRFetchContentCategoryResultAdditions)

- (NSMutableDictionary *)JSONDictionary
{
    NSUInteger const capacity = 2;
    NSMutableArray<id> *objects = [[NSMutableArray<id> alloc] initWithCapacity:capacity];
    NSMutableArray<NSString *> *keys = [[NSMutableArray<NSString *> alloc] initWithCapacity:capacity];

    [objects addObject:[self.category JSONDictionary]];
    [keys addObject:@"category"];

    NSMutableArray<NSDictionary *> *streamingServices = [[NSMutableArray<NSDictionary *> alloc] initWithCapacity:self.services.count];
    for (YCSFetchContentCategoryServiceResult *service in self.services)
        [streamingServices addObject:[service JSONDictionary]];

    [objects addObject:streamingServices];
    [keys addObject:@"services"];

    return [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
}

@end


@implementation YCSSearchContentServiceResult (YCSRSearchContentServiceResultAdditions)

- (NSMutableDictionary *)JSONDictionary
{
    NSUInteger const capacity = 2;
    NSMutableArray<id> *objects = [[NSMutableArray<id> alloc] initWithCapacity:capacity];
    NSMutableArray<NSString *> *keys = [[NSMutableArray<NSString *> alloc] initWithCapacity:capacity];

    [objects addObject:[self.service JSONDictionary]];
    [keys addObject:@"service"];

    NSMutableArray<NSDictionary *> *streamingContent = [[NSMutableArray<NSDictionary *> alloc] initWithCapacity:self.content.count];
    for (YCSStreamingContent *content in self.content)
        [streamingContent addObject:[content JSONDictionary]];

    [objects addObject:streamingContent];
    [keys addObject:@"content"];

    return [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
}

@end
