/*
 * Copyright 2025 Circle Internet Group, Inc. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cybavo.reactnative.wallet.service.view;

import com.cybavo.reactnative.wallet.service.PinSecretBridge;
import com.cybavo.wallet.service.auth.PinSecret;
import com.cybavo.wallet.service.view.NumericPinCodeInputView;
import com.cybavo.wallet.service.view.PinCodeInputView;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.BaseViewManager;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewDefaults;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;

import java.util.Map;

public class NumericPinCodeInputViewManager
        extends BaseViewManager<NumericPinCodeInputView, NumericPinCodeInputViewShadowNode> {

    private static final int COMMAND_SUBMIT = 1;
    private static final int COMMAND_CLEAR = 2;
    private static final int COMMAND_SUBMIT_FOR_MULTIPLE = 3;
    private static final int COMMAND_SUBMIT_PLAIN = 4;
    private static final int COMMAND_GET_STRENGTH_LEVEL = 5;
    private static final int COMMAND_IS_SAME_PIN = 6;

    private static final String REACT_CLASS = "NumericPinCodeInputView";

    @Override
    public String getName() {
        return REACT_CLASS;
    }

    @Override
    protected NumericPinCodeInputView createViewInstance(ThemedReactContext themedReactContext) {
        return createView(themedReactContext);
    }

    @Override
    public NumericPinCodeInputViewShadowNode createShadowNodeInstance() {
        return new NumericPinCodeInputViewShadowNode();
    }

    @Override
    public Class<NumericPinCodeInputViewShadowNode> getShadowNodeClass() {
        return NumericPinCodeInputViewShadowNode.class;
    }

    @Override
    public void updateExtraData(NumericPinCodeInputView root, Object extraData) {
        // do nothing
    }

    // bridge properties
    @ReactProp(name = "keepKey")
    public void setKeepKey(NumericPinCodeInputView view, boolean keepKey) {
        view.setKeepKey(keepKey);
    }

    @ReactProp(name = "maxLength", defaultInt = 0)
    public void setMaxLength(NumericPinCodeInputView view, int length) {
        if (length > 0) {
            view.setMaxLength(length);
        }
    }

    @ReactProp(name = "fixedOrder")
    public void setFixedOrder(NumericPinCodeInputView view, boolean fixedOrder) {
        view.setFixedOrder(fixedOrder);
    }

    @ReactProp(name = "hapticFeedback")
    public void setHapticFeedback(NumericPinCodeInputView view, boolean hapticFeedback) {
        view.setHapticFeedback(hapticFeedback);
    }

    @ReactProp(name = "horizontalSpacing")
    public void setHorizontalSpacing(NumericPinCodeInputView view, float spacing) {
        view.setHorizontalSpacing((int) PixelUtil.toPixelFromDIP(spacing));
    }

    @ReactProp(name = "verticalSpacing")
    public void setVerticalSpacing(NumericPinCodeInputView view, float spacing) {
        view.setVerticalSpacing((int) PixelUtil.toPixelFromDIP(spacing));
    }

    @ReactProp(name = "backspaceButtonText")
    public void setBackspaceButtonText(NumericPinCodeInputView view, String text) {
        view.setBackspaceButtonText(text);
    }

    @ReactPropGroup(
            names = {
                    "androidButtonRippleColor",
                    "androidBackspaceButtonRippleColor",
            },
            customType = "Color"
    )
    public void setRippleColor(NumericPinCodeInputView view, int index, Integer color) {
        if (index == 0) {
            ButtonPropertyHolder.setRippleColor(view, color);
        } else if (index == 1) {
            ButtonPropertyHolder.setBackspaceRippleColor(view, color);
        }
    }

    @ReactPropGroup(
            names = {
                    "buttonWidth",
                    "backspaceButtonWidth",
            },
            defaultFloat = ButtonPropertyHolder.UNDEFINED_INT
    )
    public void setButtonWidth(NumericPinCodeInputView view, int index, float width) {
        if (index == 0) {
            ButtonPropertyHolder.setWidth(view, (int) PixelUtil.toPixelFromDIP(width));
        } else if (index == 1) {
            ButtonPropertyHolder.setBackspaceWidth(view, (int) PixelUtil.toPixelFromDIP(width));
        }
    }

    @ReactPropGroup(
            names = {
                    "buttonHeight",
                    "backspaceButtonHeight",
            },
            defaultFloat = ButtonPropertyHolder.UNDEFINED_INT
    )
    public void setButtonHeight(NumericPinCodeInputView view, int index, float height) {
        if (index == 0) {
            ButtonPropertyHolder.setHeight(view, (int) PixelUtil.toPixelFromDIP(height));
        } else if (index == 1) {
            ButtonPropertyHolder.setBackspaceHeight(view, (int) PixelUtil.toPixelFromDIP(height));
        }
    }

    @ReactPropGroup(
            names = {
                    "buttonBorderRadius",
                    "backspaceButtonBorderRadius",
            },
            defaultFloat = ButtonPropertyHolder.UNDEFINED_INT
    )
    public void setButtonBorderRadius(NumericPinCodeInputView view, int index, float borderRadius) {
        if (index == 0) {
            ButtonPropertyHolder.setBorderRadius(view, (int) PixelUtil.toPixelFromDIP(borderRadius));
        } else if (index == 1) {
            ButtonPropertyHolder.setBackspaceBorderRadius(view, (int) PixelUtil.toPixelFromDIP(borderRadius));
        }
    }

    @ReactPropGroup(
            names = {
                    "buttonBorderWidth",
                    "backspaceButtonBorderWidth",
            },
            defaultFloat = ButtonPropertyHolder.UNDEFINED_INT
    )
    public void setButtonBorderWidth(NumericPinCodeInputView view, int index, float borderWidth) {
        if (index == 0) {
            ButtonPropertyHolder.setBorderWidth(view, (int) PixelUtil.toPixelFromDIP(borderWidth));
        } else if (index == 1) {
            ButtonPropertyHolder.setBackspaceBorderWidth(view, (int) PixelUtil.toPixelFromDIP(borderWidth));
        }
    }

    @ReactPropGroup(
            names = {
                    "buttonTextSize",
                    "backspaceButtonTextSize",
            },
            defaultFloat = ViewDefaults.FONT_SIZE_SP
    )
    public void setButtonTextSize(NumericPinCodeInputView view, int index, float textSize) {
        if (index == 0) {
            ButtonPropertyHolder.setTextSize(view, PixelUtil.toPixelFromSP(textSize));
        } else if (index == 1) {
            ButtonPropertyHolder.setBackspaceTextSize(view, PixelUtil.toPixelFromSP(textSize));
        }
    }

    @ReactPropGroup(
            names = {
                    "buttonBackgroundColor",
                    "buttonBackgroundColorPressed",
                    "buttonBackgroundColorDisabled",
                    "backspaceButtonBackgroundColor",
                    "backspaceButtonBackgroundColorPressed",
                    "backspaceButtonBackgroundColorDisabled",
            },
            customType = "Color"
    )
    public void setButtonBackgroundColor(NumericPinCodeInputView view, int index, Integer color) {
        if (index == 0) {
            ButtonPropertyHolder.setBackgroundColor(view, color);
        } else if (index == 1) {
            ButtonPropertyHolder.setBackgroundColorPressed(view, color);
        } else if (index == 2) {
            ButtonPropertyHolder.setBackgroundColorDisabled(view, color);
        } else if (index == 3) {
            ButtonPropertyHolder.setBackspaceBackgroundColor(view, color);
        } else if (index == 4) {
            ButtonPropertyHolder.setBackspaceBackgroundColorPressed(view, color);
        } else if (index == 5) {
            ButtonPropertyHolder.setBackspaceBackgroundColorDisabled(view, color);
        }
    }

    @ReactPropGroup(
            names = {
                    "buttonBorderColor",
                    "buttonBorderColorPressed",
                    "buttonBorderColorDisabled",
                    "backspaceButtonBorderColor",
                    "backspaceButtonBorderColorPressed",
                    "backspaceButtonBorderColorDisabled",
            },
            customType = "Color"
    )
    public void setButtonBorderColor(NumericPinCodeInputView view, int index, Integer color) {
        if (index == 0) {
            ButtonPropertyHolder.setBorderColor(view, color);
        } else if (index == 1) {
            ButtonPropertyHolder.setBorderColorPressed(view, color);
        } else if (index == 2) {
            ButtonPropertyHolder.setBorderColorDisabled(view, color);
        } else if (index == 3) {
            ButtonPropertyHolder.setBackspaceBorderColor(view, color);
        } else if (index == 4) {
            ButtonPropertyHolder.setBackspaceBorderColorPressed(view, color);
        } else if (index == 5) {
            ButtonPropertyHolder.setBackspaceBorderColorDisabled(view, color);
        }
    }

    @ReactPropGroup(
            names = {
                    "buttonTextColor",
                    "buttonTextColorPressed",
                    "buttonTextColorDisabled",
                    "backspaceButtonTextColor",
                    "backspaceButtonTextColorPressed",
                    "backspaceButtonTextColorDisabled",
            },
            customType = "Color"
    )
    public void setButtonTextColor(NumericPinCodeInputView view, int index, Integer color) {
        if (index == 0) {
            ButtonPropertyHolder.setTextColor(view, color);
        } else if (index == 1) {
            ButtonPropertyHolder.setTextColorPressed(view, color);
        } else if (index == 2) {
            ButtonPropertyHolder.setTextColorDisabled(view, color);
        } else if (index == 3) {
            ButtonPropertyHolder.setBackspaceTextColor(view, color);
        } else if (index == 4) {
            ButtonPropertyHolder.setBackspaceTextColorPressed(view, color);
        } else if (index == 5) {
            ButtonPropertyHolder.setBackspaceTextColorDisabled(view, color);
        }
    }

    // events bridging
    @Override
    public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
        return MapBuilder.<String, Object>of(
                PinCodeChangedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onChanged"),
                SubmitPlainEvent.EVENT_NAME, MapBuilder.of("registrationName", "onSubmitPlain"),
                SubmitEvent.EVENT_NAME, MapBuilder.of("registrationName", "onSubmit"),
                GetStrengthLevelEvent.EVENT_NAME, MapBuilder.of("registrationName", "onStrengthLevel"),
                IsSamePinEvent.EVENT_NAME, MapBuilder.of("registrationName", "onSamePin")
        );
    }

    @Override
    protected void addEventEmitters(final ThemedReactContext reactContext, final NumericPinCodeInputView view) {
        view.setOnPinCodeInputListener(new PinCodeInputView.OnPinCodeInputListener() {
            @Override
            public void onChanged(int length) {
                reactContext
                        .getNativeModule(UIManagerModule.class)
                        .getEventDispatcher()
                        .dispatchEvent(new PinCodeChangedEvent(view.getId(), length));
            }
        });
    }

    // commands bridging
    @Override
    public Map<String, Integer> getCommandsMap() {
        return MapBuilder.of(
                "submit", COMMAND_SUBMIT,
                "submitForMultiple", COMMAND_SUBMIT_FOR_MULTIPLE,
                "submitPlain", COMMAND_SUBMIT_PLAIN,
                "clear", COMMAND_CLEAR,
                "getStrengthLevel", COMMAND_GET_STRENGTH_LEVEL,
                "isSamePin", COMMAND_IS_SAME_PIN
                );
    }

    @Override
    public void receiveCommand(NumericPinCodeInputView view, int commandType, ReadableArray args) {
        int requestId;
        PinSecret pinSecret;
        ReactContext reactContext;
        switch (commandType) {
            case COMMAND_SUBMIT:
                requestId = args.getInt(0);
                pinSecret = view.submit();
                final int key = PinSecretBridge.put(pinSecret);

                reactContext = (ReactContext) view.getContext();
                reactContext
                        .getNativeModule(UIManagerModule.class)
                        .getEventDispatcher()
                        .dispatchEvent(new SubmitEvent(view.getId(), requestId, key));
                return;
            case COMMAND_SUBMIT_FOR_MULTIPLE:
                requestId = args.getInt(0);
                pinSecret = view.submitForMultiple();
                final int key1 = PinSecretBridge.put(pinSecret);

                reactContext = (ReactContext) view.getContext();
                reactContext
                        .getNativeModule(UIManagerModule.class)
                        .getEventDispatcher()
                        .dispatchEvent(new SubmitEvent(view.getId(), requestId, key1));
                return;
            case COMMAND_SUBMIT_PLAIN:
                requestId = args.getInt(0);
                final String text = view.submitPlain();
                reactContext = (ReactContext) view.getContext();
                reactContext
                        .getNativeModule(UIManagerModule.class)
                        .getEventDispatcher()
                        .dispatchEvent(new SubmitPlainEvent(view.getId(), requestId, text));
                return;
            case COMMAND_GET_STRENGTH_LEVEL:
                requestId = args.getInt(0);
                int length = args.getInt(1);
                final int level = view.getStrengthLevel(length);
                reactContext = (ReactContext) view.getContext();
                reactContext
                        .getNativeModule(UIManagerModule.class)
                        .getEventDispatcher()
                        .dispatchEvent(new GetStrengthLevelEvent(view.getId(), requestId, level));
                return;
            case COMMAND_IS_SAME_PIN:
                requestId = args.getInt(0);
                ReadableMap psMap1 = args.getMap(1);
                ReadableMap psMap2 = args.getMap(2);
                PinSecret pinSecret1 = PinSecretBridge.fromReadableMap(psMap1);
                PinSecret pinSecret2 = PinSecretBridge.fromReadableMap(psMap2);
                final boolean isSame = view.isSamePin(pinSecret1, pinSecret2);
                reactContext = (ReactContext) view.getContext();
                reactContext
                        .getNativeModule(UIManagerModule.class)
                        .getEventDispatcher()
                        .dispatchEvent(new IsSamePinEvent(view.getId(), requestId, isSame));
                return;
            case COMMAND_CLEAR:
                view.clear();
                return;
            default:
                throw new IllegalArgumentException(
                        String.format(
                                "Unsupported command %d received by %s.", commandType, getClass().getSimpleName()));
        }
    }

    private NumericPinCodeInputView createView(ThemedReactContext context) {
        return new NumericPinCodeInputView(context);
    }
}
