/*******************************************************
 * Copyright (C) 2018 XMind Ltd. - All Rights Reserved
 *******************************************************/

#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <napi.h>
#import "./sandbox.h"

// 辅助函数：将 N-API 字符串转换为 NSString
NSString* ToNSString(const std::string& value) {
  return [[NSString alloc] initWithUTF8String:value.c_str()];
}

Napi::Value SbCreateBookmarkDataForSecurityScopedResource(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 1 || !info[0].IsString()) {
    Napi::TypeError::New(env, "Invalid arguments").ThrowAsJavaScriptException();
    return env.Undefined();
  }

  std::string filePath = info[0].As<Napi::String>().Utf8Value();

  @autoreleasepool {
    NSString* nsFilePath = ToNSString(filePath);
    NSError* error;

    NSURL* url = [NSURL fileURLWithPath:nsFilePath];
    NSData* bookmarkData = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                         includingResourceValuesForKeys:nil
                                          relativeToURL:nil
                                                  error:&error];

    if (error != nil) {
      return Napi::String::New(env, [[error description] UTF8String]);
    }

    Napi::Object obj = Napi::Object::New(env);
    obj.Set("bookmarkData", Napi::String::New(env,
        [[bookmarkData base64EncodedStringWithOptions:0] UTF8String]));

    return obj;
  }
}

Napi::Value SbResolveBookmarkDataForSecurityScopedResource(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 1 || !info[0].IsString()) {
    Napi::TypeError::New(env, "Invalid arguments").ThrowAsJavaScriptException();
    return env.Undefined();
  }

  std::string bookmarkDataStr = info[0].As<Napi::String>().Utf8Value();

  @autoreleasepool {
    NSString* nsBookmarkDataStr = ToNSString(bookmarkDataStr);
    NSData* bookmarkData = [[NSData alloc] initWithBase64EncodedString:nsBookmarkDataStr
                                                               options:0];

    BOOL isStale = false;
    NSError* error = nil;

    NSURL* url = [NSURL URLByResolvingBookmarkData:bookmarkData
                                           options:NSURLBookmarkResolutionWithSecurityScope
                                     relativeToURL:nil
                               bookmarkDataIsStale:&isStale
                                             error:&error];

    if (error != nil) {
      return Napi::String::New(env, [[error description] UTF8String]);
    }

    NSString* newBookmarkData = nil;
    if (isStale) {
      if ([url startAccessingSecurityScopedResource]) {
        error = nil;
        NSData* newData = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                        includingResourceValuesForKeys:nil
                                         relativeToURL:nil
                                                 error:&error];
        [url stopAccessingSecurityScopedResource];

        if (error == nil) {
          newBookmarkData = [newData base64EncodedStringWithOptions:0];
        }
      }
    }

    Napi::Object obj = Napi::Object::New(env);
    obj.Set("filePath", Napi::String::New(env, [url.path UTF8String]));
    obj.Set("newBookmarkData", Napi::String::New(env,
        newBookmarkData == nil ? "" : [newBookmarkData UTF8String]));

    return obj;
  }
}

Napi::Value SbStartAccessingSecurityScopedResource(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 1 || !info[0].IsString()) {
    Napi::TypeError::New(env, "Invalid arguments").ThrowAsJavaScriptException();
    return env.Undefined();
  }

  std::string bookmarkDataStr = info[0].As<Napi::String>().Utf8Value();

  @autoreleasepool {
    NSString* nsBookmarkDataStr = ToNSString(bookmarkDataStr);
    NSData* bookmarkData = [[NSData alloc] initWithBase64EncodedString:nsBookmarkDataStr
                                                               options:0];

    BOOL isStale = false;
    NSError* error = nil;

    NSURL* url = [NSURL URLByResolvingBookmarkData:bookmarkData
                                           options:NSURLBookmarkResolutionWithSecurityScope
                                     relativeToURL:nil
                               bookmarkDataIsStale:&isStale
                                             error:&error];

    if (error != nil) {
      return Napi::String::New(env, [[error description] UTF8String]);
    } else {
      BOOL result = [url startAccessingSecurityScopedResource];
      return Napi::Boolean::New(env, (bool)result);
    }
  }
}

Napi::Value SbStopAccessingSecurityScopedResource(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 1 || !info[0].IsString()) {
    Napi::TypeError::New(env, "Invalid arguments").ThrowAsJavaScriptException();
    return env.Undefined();
  }

  std::string bookmarkDataStr = info[0].As<Napi::String>().Utf8Value();

  @autoreleasepool {
    NSString* nsBookmarkDataStr = ToNSString(bookmarkDataStr);
    NSData* bookmarkData = [[NSData alloc] initWithBase64EncodedString:nsBookmarkDataStr
                                                               options:0];

    BOOL isStale = false;
    NSError* error = nil;

    NSURL* url = [NSURL URLByResolvingBookmarkData:bookmarkData
                                           options:NSURLBookmarkResolutionWithSecurityScope
                                     relativeToURL:nil
                               bookmarkDataIsStale:&isStale
                                             error:&error];

    if (error == nil) {
      [url stopAccessingSecurityScopedResource];
    }

    return env.Undefined();
  }
}