//
//  Copyright © 2018-2025 PSPDFKit GmbH. All rights reserved.
//
//  THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
//  AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
//  UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
//  This notice may not be removed from this file.
//

#if RCT_NEW_ARCH_ENABLED

// Needed for Swift header to compile
#import <PSPDFKit/PSPDFKit.h>
#import <PSPDFKitUI/PSPDFKitUI.h>
#import <Instant/Instant.h>

#import "NutrientView.h"
#import "RCTPSPDFKitView.h"
#if __has_include("PSPDFKitReactNativeiOS-Swift.h")
#import "PSPDFKitReactNativeiOS-Swift.h"
#else
#import <PSPDFKitReactNativeiOS/PSPDFKitReactNativeiOS-Swift.h>
#endif
#import "NutrientPropsDocumentHelper.h"
#import "NutrientPropsToolbarHelper.h"
#import "NutrientPropsAnnotationsHelper.h"
#import "NutrientPropsFontHelper.h"
#import "NutrientPropsUIHelper.h"
#import "NutrientViewRegistry.h"
#import "NutrientFabricUtils.h"

#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>
#import <React/RCTViewComponentView.h>
#import <React/UIView+React.h>

#import <React/RCTBridge.h>

// Codegen headers (generated by RN based on package.json codegenConfig)
#import <react/renderer/components/nutrient_sdk_react_native_codegen/ComponentDescriptors.h>
#import <react/renderer/components/nutrient_sdk_react_native_codegen/Props.h>
#import <react/renderer/components/nutrient_sdk_react_native_codegen/EventEmitters.h>
#import <react/renderer/components/nutrient_sdk_react_native_codegen/RCTComponentViewHelpers.h>

using namespace facebook::react;

// Currently there is no way to access the Turbo / Native modules, aside from using the bridge
@interface RCTBridge (Private)
+ (RCTBridge *)currentBridge;
@end

@interface NutrientView () <RCTNutrientViewViewProtocol, RCTComponentViewProtocol, RCTPSPDFKitViewDelegate>
@end

@implementation NutrientView {
    RCTPSPDFKitView *_view;
    std::shared_ptr<const NutrientViewEventEmitter> _eventEmitter;
    
    // Basic document props
    NSString *_document;
    
    // Document configuration
    NSString *_configurationJSONString;
    NSString *_toolbarJSONString;
    NSString *_menuItemGroupingJSONString;
    NutrientViewAnnotationPresetsStruct _annotationPresets;
    
    // Basic document navigation
    NSInteger _pageIndex;
    NSString *_fragmentTag;
    
    // Basic styling and layout
    BOOL _hideNavigationBar;
    
    // Basic interaction settings
    BOOL _showCloseButton;
    
    // Props from Paper ViewManager that are Codegen-compatible
    BOOL _disableDefaultActionForTappedAnnotations;
    NSString *_annotationAuthorName;
    NSString *_imageSaveMode;
    BOOL _showNavigationButtonInToolbar;
    NSString *_selectedFontName;
    NSString *_availableFontNamesJSONString;
    BOOL _showDownloadableFonts;
}

// MARK: - Helpers (C++/ObjC++)

- (instancetype)initWithFrame:(CGRect)frame
{
  if (self = [super initWithFrame:frame]) {
    
    // Initialize properties with default values
    _pageIndex = 0;

    // Create a RCTPSPDFKitView instance that manages our PSPDFKit ViewController
    _view = [[RCTPSPDFKitView alloc] initWithFrame:frame];
    _view.delegate = self;
    self.contentView = _view;

    _view.translatesAutoresizingMaskIntoConstraints = NO;
     [NSLayoutConstraint activateConstraints:@[
       [_view.topAnchor constraintEqualToAnchor:self.topAnchor],
       [_view.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
       [_view.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
       [_view.trailingAnchor constraintEqualToAnchor:self.trailingAnchor]
     ]];
  }
  return self;
}

#pragma mark - RCTPSPDFKitViewDelegate

- (void)pspdfViewDidDocumentLoad:(RCTPSPDFKitView *)view {
  if (_eventEmitter) {
    facebook::react::NutrientViewEventEmitter::OnDocumentLoaded payload{};
    _eventEmitter->onDocumentLoaded(payload);
  }
}

- (void)pspdfViewDidReady:(RCTPSPDFKitView *)view {
  if (_eventEmitter) {
    facebook::react::NutrientViewEventEmitter::OnReady payload{};
    _eventEmitter->onReady(payload);
  }
}

- (void)pspdfView:(RCTPSPDFKitView *)view didChangeState:(NSDictionary *)_unusedStateDict {
  if (!_eventEmitter) { return; }

  BOOL documentLoaded = [view.pdfController.document isValid];
  PSPDFPageCount pageCount = view.pdfController.document.pageCount;
  BOOL annotationCreationActive = [view.pdfController.annotationToolbarController isToolbarVisible];
  PSPDFPageIndex currentPageIndex = view.pdfController.pageIndex;
  PSPDFPageIndex affectedPageIndex = currentPageIndex;

  BOOL annotationEditingActive = NO;
  BOOL textSelectionActive = NO;
  BOOL formEditingActive = NO;

  PSPDFPageView *pageView = [view.pdfController pageViewForPageAtIndex:currentPageIndex];
  if (pageView) {
    annotationEditingActive = pageView.selectedAnnotations.count > 0;
    textSelectionActive = pageView.selectionView.selectedText.length > 0;
    for (PSPDFAnnotation *annotation in pageView.selectedAnnotations) {
      if ([annotation isKindOfClass:PSPDFWidgetAnnotation.class]) { formEditingActive = YES; break; }
    }
  }

  // Emit flat structure matching Android (no nested "state" object)
  facebook::react::NutrientViewEventEmitter::OnStateChanged payload{
    (bool)documentLoaded,
    (int)currentPageIndex,
    (int)pageCount,
    (bool)annotationCreationActive,
    (int)affectedPageIndex,
    (bool)annotationEditingActive,
    (bool)textSelectionActive,
    (bool)formEditingActive
  };
  _eventEmitter->onStateChanged(payload);
}

- (void)pspdfView:(RCTPSPDFKitView *)view didTapCustomToolbarButtonWithId:(NSString *)buttonId {
  if (_eventEmitter) {
    std::string val = buttonId ? std::string([buttonId UTF8String]) : std::string("");
    facebook::react::NutrientViewEventEmitter::OnCustomToolbarButtonTapped payload{ val, val };
    _eventEmitter->onCustomToolbarButtonTapped(payload);
  }
}

- (void)pspdfView:(RCTPSPDFKitView *)view didTapCustomAnnotationMenuItemWithId:(NSString *)itemId {
  if (_eventEmitter) {
    facebook::react::NutrientViewEventEmitter::OnCustomAnnotationContextualMenuItemTapped payload{
      itemId ? std::string([itemId UTF8String]) : std::string("")
    };
    _eventEmitter->onCustomAnnotationContextualMenuItemTapped(payload);
  }
}

// Forward annotation change events to JS (Fabric) using codegen types
- (void)pspdfView:(RCTPSPDFKitView *)view didChangeAnnotationsWithChange:(NSString *)change annotationsJSONString:(NSString *)annotationsJSONString {
  if (_eventEmitter) {
    std::string changeStr = change ? std::string([change UTF8String]) : std::string("");
    std::string jsonStr = annotationsJSONString ? std::string([annotationsJSONString UTF8String]) : std::string("[]");
    facebook::react::NutrientViewEventEmitter::OnAnnotationsChanged payload{ changeStr, jsonStr };
    _eventEmitter->onAnnotationsChanged(payload);
  }
}

- (void)pspdfViewDidPressCloseButton:(RCTPSPDFKitView *)view {
  if (_eventEmitter) {
    facebook::react::NutrientViewEventEmitter::OnCloseButtonPressed payload{};
    _eventEmitter->onCloseButtonPressed(payload);
  }
}

- (void)pspdfView:(RCTPSPDFKitView *)view didFailToLoadDocumentWithError:(NSString *)error {
  if (_eventEmitter) {
    NSString *err = error ?: @"Unknown error";
    // Determine error code based on error message or document state
    // Check if document is encrypted (password required)
    NSString *code = @"CORRUPTED"; // Default to CORRUPTED
    if (view.pdfController.document && view.pdfController.document.isEncrypted) {
      code = @"ENCRYPTED";
    } else if ([err localizedCaseInsensitiveContainsString:@"password"] || 
               [err localizedCaseInsensitiveContainsString:@"encrypt"]) {
      code = @"ENCRYPTED";
    }
    
    facebook::react::NutrientViewEventEmitter::OnDocumentLoadFailed payload{
      std::string([code UTF8String]),
      std::string([err UTF8String])
    };
    _eventEmitter->onDocumentLoadFailed(payload);
  }
}

- (void)pspdfViewDidSaveDocument:(RCTPSPDFKitView *)view {
  if (_eventEmitter) {
    facebook::react::NutrientViewEventEmitter::OnDocumentSaved payload{};
    _eventEmitter->onDocumentSaved(payload);
  }
}

- (void)pspdfView:(RCTPSPDFKitView *)view didFailToSaveDocumentWithError:(NSString *)error {
  if (_eventEmitter) {
    NSString *err = error ?: @"Unknown error";
    auto payload = facebook::react::NutrientViewEventEmitter::OnDocumentSaveFailed{
      std::string([err UTF8String])
    };
    _eventEmitter->onDocumentSaveFailed(payload);
  }
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
  [super updateProps:props oldProps:oldProps];
    
    // Update our reference of Views
    [[NutrientViewRegistry shared] registerView:_view withId:self.nativeId];
    // Set a unified integer component identifier from nativeID under New Architecture
    _view.componentID = [self.nativeId integerValue];
    
  auto newProps = std::static_pointer_cast<const NutrientViewProps>(props);
  if (newProps != nullptr) {
      // Basic document props
      if (!newProps->document.empty()) {
          _document = RCTNSStringFromString(newProps->document);
          PDFDocumentManager *documentManager = [[RCTBridge currentBridge] moduleForClass:[PDFDocumentManager class]];
          [NutrientPropsDocumentHelper applyDocumentFromJSON:_document toView:_view usingManager:documentManager withReference:[NSNumber numberWithInteger:[self.nativeId integerValue]]];
          [NutrientPropsFontHelper configureCustomFontPickerForView:_view];
      }
          
      // Basic document navigation
      _pageIndex = newProps->pageIndex;
      [NutrientPropsDocumentHelper applyPageIndexFromJSON:[NSNumber numberWithInteger:_pageIndex] toView:_view];
    
      _hideNavigationBar = newProps->hideNavigationBar;
      _showCloseButton = newProps->showCloseButton;
      [NutrientPropsUIHelper applyShowCloseButtonFromJSON:@(_showCloseButton) toView:_view];
      _disableDefaultActionForTappedAnnotations = newProps->disableDefaultActionForTappedAnnotations;
      _view.disableDefaultActionForTappedAnnotations = _disableDefaultActionForTappedAnnotations;
      _showNavigationButtonInToolbar = newProps->showNavigationButtonInToolbar;
      if (!newProps->availableFontNamesJSONString.empty()) {
        _availableFontNamesJSONString = RCTNSStringFromString(newProps->availableFontNamesJSONString);
        NSArray<NSString *> *arr = [NutrientFabricUtils stringArrayFromJSONString:_availableFontNamesJSONString];
        if (arr) {
          NSDictionary *wrapped = @{ @"availableFontNames": arr };
          [NutrientPropsFontHelper applyAvailableFontNamesFromJSON:wrapped toView:_view];
        }
      }
      if (!newProps->selectedFontName.empty()) {
        _selectedFontName = RCTNSStringFromString(newProps->selectedFontName);
        [NutrientPropsFontHelper applySelectedFontNameFromJSON:_selectedFontName toView:_view];
      }
      
      _showDownloadableFonts = newProps->showDownloadableFonts;
      [NutrientPropsFontHelper applyShowDownloadableFontsFromJSON:@(_showDownloadableFonts) toView:_view];
      
      if (!newProps->menuItemGroupingJSONString.empty()) {
          _menuItemGroupingJSONString = RCTNSStringFromString(newProps->menuItemGroupingJSONString);
          id root = [NutrientFabricUtils objectFromJSONString:_menuItemGroupingJSONString];
          if ([root isKindOfClass:[NSArray class]]) {
            // Accept arrays of strings/objects/mixed directly
            [NutrientPropsDocumentHelper applyMenuItemGroupingFromJSON:(NSArray *)root toView:_view];
          }
      }
      
      if (!newProps->annotationAuthorName.empty()) {
          _annotationAuthorName = RCTNSStringFromString(newProps->annotationAuthorName);
          [NutrientPropsDocumentHelper applyAnnotationAuthorNameFromJSON:_annotationAuthorName toView:_view];
      }
    
      if (!newProps->imageSaveMode.empty()) {
          _imageSaveMode = RCTNSStringFromString(newProps->imageSaveMode);
          [NutrientPropsDocumentHelper applyImageSaveModeFromJSON:_imageSaveMode toView:_view];
      }

      // Only use JSON passthrough; omit entirely if not provided so native defaults apply
      if (!newProps->configurationJSONString.empty()) {
          _configurationJSONString = RCTNSStringFromString(newProps->configurationJSONString);
          NSDictionary *jsonConfig = [NutrientFabricUtils dictionaryFromJSONString:_configurationJSONString];
          if (jsonConfig) {
              [NutrientPropsDocumentHelper applyConfigurationFromJSON:jsonConfig toView:_view];
          }
      }
      
      // Handle toolbar JSON string
      if (!newProps->toolbarJSONString.empty()) {
          _toolbarJSONString = RCTNSStringFromString(newProps->toolbarJSONString);
          NSDictionary *toolbarDict = [NutrientFabricUtils dictionaryFromJSONString:_toolbarJSONString];
          if (toolbarDict) {
              [NutrientPropsToolbarHelper applyToolbarFromJSON:toolbarDict toView:_view];
          }
      }
      
      // Handle annotation presets (struct-based)
      _annotationPresets = newProps->annotationPresets;
      NSDictionary *presetsDict = [NutrientFabricUtils convertAnnotationPresetsToDictionary:&_annotationPresets];
      if (presetsDict) {
          [NutrientPropsDocumentHelper applyAnnotationPresetsFromJSON:presetsDict toView:_view];
      }
      
      // Handle annotation contextual menu via JSON string (presence aware)
      if (!newProps->annotationContextualMenuJSONString.empty()) {
        NSString *menuJSON = RCTNSStringFromString(newProps->annotationContextualMenuJSONString);
        NSDictionary *contextualMenuDict = [NutrientFabricUtils dictionaryFromJSONString:menuJSON];
        if (contextualMenuDict) {
          [NutrientPropsAnnotationsHelper applyAnnotationContextualMenuFromJSON:contextualMenuDict toView:_view];
        }
      }
  }
}

- (BOOL)isEventEmitterReady {
  return _eventEmitter != nullptr;
}

- (void)updateEventEmitter:(facebook::react::EventEmitter::Shared const &)eventEmitter {
    [super updateEventEmitter:eventEmitter];
    _eventEmitter = std::static_pointer_cast<const NutrientViewEventEmitter>(eventEmitter);
    [_view processPendingCallbacks];
}

- (void)dealloc
{
  // Unregister this view from the registry when deallocated
  if (self.nativeId) {
    [[NutrientViewRegistry shared] unregisterViewWithId:self.nativeId];
  }
  
  // Clean up delegate reference
  if (_view) {
    _view.delegate = nil;
  }
  
  // Clear event emitter
  _eventEmitter = nullptr;
}

+ (BOOL)shouldBeRecycled
{
    return NO;
}

- (void)prepareForRecycle
{
  [super prepareForRecycle];
  
  // Unregister this view from the registry when recycled
  if (self.nativeId) {
    [[NutrientViewRegistry shared] unregisterViewWithId:self.nativeId];
  }
  
  // Reset the view state to ensure clean reuse
  if (_view) {
    // Reset any state that might persist between reuses
    _view.delegate = nil;
    
    // Reset any cached properties
    _document = nil;
    _configurationJSONString = nil;
    _toolbarJSONString = nil;
    _menuItemGroupingJSONString = nil;
    _fragmentTag = nil;
    
    // Reset any other state that should be clean for the next use
    _pageIndex = 0;
    _annotationAuthorName = nil;
    _imageSaveMode = nil;
    _selectedFontName = nil;
    _availableFontNamesJSONString = nil;
    _showDownloadableFonts = NO;

    // Re-setup the delegate for the new instance
    _view.delegate = self;
  }
  
  // Reset event emitter
  _eventEmitter = nullptr;
}

+ (ComponentDescriptorProvider)componentDescriptorProvider
{
  return concreteComponentDescriptorProvider<NutrientViewComponentDescriptor>();
}

@end

Class<RCTComponentViewProtocol> NutrientViewCls(void) {
  return NutrientView.class;
}

#endif
