package com.qliktrialreactnativestraighttable;

import android.annotation.SuppressLint;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

public class ReactNativeStraightTableViewManager extends SimpleViewManager<View> {

  public static final String REACT_CLASS = "ReactNativeStraightTableView";
    @Override
    @NonNull
    public String getName() {
        return REACT_CLASS;
    }

    @SuppressLint("NewApi")
    @Override
    @NonNull
    public View createViewInstance(ThemedReactContext reactContext) {
      return new TableView(reactContext);
    }

    private boolean processRows(TableView tableView, ReadableMap rows) {
      // Try new format first (rowsJSON string)
      if (rows.hasKey("rowsJSON")) {
        String rowsJSON = rows.getString("rowsJSON");
        if (rowsJSON != null && !rowsJSON.isEmpty()) {
          try {
            org.json.JSONArray jsonArray = new org.json.JSONArray(rowsJSON);
            com.facebook.react.bridge.WritableArray writableArray = com.facebook.react.bridge.Arguments.createArray();
            
            // Manually convert JSONArray to WritableArray
            for (int i = 0; i < jsonArray.length(); i++) {
              org.json.JSONObject jsonObject = jsonArray.getJSONObject(i);
              com.facebook.react.bridge.WritableMap writableMap = com.facebook.react.bridge.Arguments.createMap();
              
              // Iterate through all keys in the JSON object
              java.util.Iterator<String> keys = jsonObject.keys();
              while (keys.hasNext()) {
                String key = keys.next();
                Object value = jsonObject.get(key);
                
                if (value instanceof String) {
                  writableMap.putString(key, (String) value);
                } else if (value instanceof Integer) {
                  writableMap.putInt(key, (Integer) value);
                } else if (value instanceof Double) {
                  writableMap.putDouble(key, (Double) value);
                } else if (value instanceof Boolean) {
                  writableMap.putBoolean(key, (Boolean) value);
                } else if (value instanceof org.json.JSONObject) {
                  writableMap.putMap(key, convertJsonToMap((org.json.JSONObject) value));
                } else if (value == org.json.JSONObject.NULL) {
                  writableMap.putNull(key);
                }
              }
              writableArray.pushMap(writableMap);
            }
            
            boolean resetData = rows.hasKey("reset") && rows.getBoolean("reset");
            RowFactory factory = new RowFactory(writableArray, tableView.getColumns());
            List<DataRow> transformedRows = factory.getRows();
            tableView.setRows(transformedRows, resetData);
            return true;
          } catch (Exception e) {
            e.printStackTrace();
            return false;
          }
        }
      }
      
      // Fall back to old format (rows array)
      ReadableArray dataRows = rows.getArray("rows");
      if(dataRows != null) {
        boolean resetData = rows.getBoolean("reset");
        RowFactory factory = new RowFactory(dataRows, tableView.getColumns());
        List<DataRow> transformedRows = factory.getRows();
        tableView.setRows(transformedRows, resetData);
        return true;
      }
      return false;
    }
    
    private com.facebook.react.bridge.WritableMap convertJsonToMap(org.json.JSONObject jsonObject) throws org.json.JSONException {
      com.facebook.react.bridge.WritableMap map = com.facebook.react.bridge.Arguments.createMap();
      java.util.Iterator<String> keys = jsonObject.keys();
      while (keys.hasNext()) {
        String key = keys.next();
        Object value = jsonObject.get(key);
        
        if (value instanceof String) {
          map.putString(key, (String) value);
        } else if (value instanceof Integer) {
          map.putInt(key, (Integer) value);
        } else if (value instanceof Double) {
          map.putDouble(key, (Double) value);
        } else if (value instanceof Boolean) {
          map.putBoolean(key, (Boolean) value);
        } else if (value instanceof org.json.JSONObject) {
          map.putMap(key, convertJsonToMap((org.json.JSONObject) value));
        } else if (value == org.json.JSONObject.NULL) {
          map.putNull(key);
        }
      }
      return map;
    }

    private List<DataColumn> processColumns(TableView tableView, ReadableMap cols) {
      String totalsLabel = null, totalsPosition = null;
      ReadableArray totalsRows = null;

      ReadableArray columns = cols.getArray("header");
      ReadableMap totals = cols.getMap("totals");

      if(totals != null) {
        totalsPosition = totals.getString("position");
        totalsLabel = totals.getString("label");
        totalsRows = totals.getArray("rows");

        tableView.setTotals(totalsRows, totalsPosition, totalsLabel);
      }

      List<DataColumn> dataColumns = new ArrayList<>();
      if(columns != null) {
        for (int i = 0; i < columns.size(); i++) {
          DataColumn column = new DataColumn(columns.getMap(i), i);
          dataColumns.add(column);
        }
      }

      return dataColumns;
    }

    @ReactProp(name = "theme")
    public void setTheme(View view, @Nullable ReadableMap theme) {
      TableView tableView = (TableView) view;

      // Parse colors into THIS view's instance fields so two sheets never share state
      // through the static TableTheme fields.
      android.graphics.Color parsedColor = null; // unused — keep for reference
      if (theme == null) {
        tableView.instanceBackgroundColor = TableTheme.DEFAULT_BACKGROUND_COLOR;
        tableView.instanceEvenBackgroundColor = null;
      } else {
        try {
          String bgStr = JsonUtils.getString(theme, "backgroundColor");
          tableView.instanceBackgroundColor = bgStr != null
              ? android.graphics.Color.parseColor(bgStr)
              : TableTheme.DEFAULT_BACKGROUND_COLOR;
        } catch (Exception e) {
          tableView.instanceBackgroundColor = TableTheme.DEFAULT_BACKGROUND_COLOR;
        }
        try {
          String evenStr = JsonUtils.getString(theme, "even");
          tableView.instanceEvenBackgroundColor = evenStr != null
              ? android.graphics.Color.parseColor(evenStr)
              : null;
        } catch (Exception e) {
          tableView.instanceEvenBackgroundColor = null;
        }
      }

      // Still update the shared static for header/border/text colors (shared palette, non-contradicting)
      TableTheme.updateFrom(theme);

      // Propagate background to already-visible views owned by THIS tableView
      int bg = tableView.instanceBackgroundColor;
      tableView.setBackgroundColor(bg);

      if (tableView.tableViewFactory != null) {
        if (tableView.tableViewFactory.rootLayout != null) {
          tableView.tableViewFactory.rootLayout.setBackgroundColor(bg);
        }
        if (tableView.tableViewFactory.coupledRecyclerView != null) {
          tableView.tableViewFactory.coupledRecyclerView.setBackgroundColor(bg);
        }
        if (tableView.tableViewFactory.firstColumnRecyclerView != null) {
          tableView.tableViewFactory.firstColumnRecyclerView.setBackgroundColor(bg);
        }
        if (tableView.tableViewFactory.rowCountView != null) {
          tableView.tableViewFactory.rowCountView.container.setBackgroundColor(bg);
        }
        if (tableView.tableViewFactory.totalsView != null) {
          tableView.tableViewFactory.totalsView.setBackgroundColor(bg);
        }
      }

      if (tableView.dataProvider != null) {
        tableView.dataProvider.notifyDataSetChanged();
      }
    }

    @ReactProp(name = "translations")
    public void setTranslations(View view, @Nullable ReadableMap translations) {
      if(translations == null) {
        return;
      }
      TableView tableView = (TableView) (view);
      tableView.setTranslations(translations);
    }

    @ReactProp(name = "freezeFirstColumn")
    public void setFreezeFirstColumn(View view, Boolean isFreezeFirstColumn) {
      TableView tableView = (TableView) (view);
      tableView.setFirstColumnFrozen(isFreezeFirstColumn);
    }

    @ReactProp(name = "cols")
    public void setCols(View view,  @Nullable ReadableMap source) {
      if(source == null) {
        return;
      }
      TableView tableView = (TableView) (view);
      List<DataColumn> dataColumns = processColumns(tableView, source);
      if(tableView.dataProvider.dataColumns != null) {
        if(tableView.dataProvider.dataColumns.size() != dataColumns.size()) {
          ReadableArray savedTotalsRows = tableView.totalsRows;
          String savedTotalsPosition = tableView.totalsPosition;
          String savedTotalsLabel = tableView.totalsLabel;

          tableView.resetTable();

          tableView.setTotals(savedTotalsRows, savedTotalsPosition, savedTotalsLabel);

          tableView.setDataColumns(dataColumns);
        } else {
          tableView.setDataColumns(dataColumns);
        }
      } else {
        tableView.setDataColumns(dataColumns);
      }
    }

    @ReactProp(name = "isDataView")
    public void setDataView(View view, boolean isDataView) {
      TableView tableView = (TableView) (view);
      tableView.setDataView(isDataView);
    }

    @ReactProp(name = "rows")
    public void setRows(View view, @Nullable ReadableMap source) {
      if(source == null) {
        return;
      }

      TableView tableView = (TableView) (view);
      if(!processRows(tableView, source)) {
        // prevent table from being initialized if data is invalid.
        return;
      }
      // Initialize table if both columns, rows, and styles are present
      if((tableView.isInitialized() || tableView.needsReset) && 
         tableView.cellContentStyle != null && 
         tableView.headerContentStyle != null) {
        tableView.initialize();
      }
    }

    @ReactProp(name = "size")
    public void setSize(View view, @Nullable ReadableMap source) {
      if(source != null) {
        TableView tableView = (TableView) view;
        DataSize dataSize = new DataSize(source);
        tableView.setDataSize(dataSize);
      }
    }

    @ReactProp(name = "containerWidth")
    public void setContainerWidth(View view, int width) {
      // no op
    }

    @ReactProp(name = "clearSelections")
    public void setClearSelections(View view, String value) {
      if (value.equalsIgnoreCase("yes")) {
        TableView tableView = (TableView) (view);
        tableView.clearSelections();
      }
    }

    @ReactProp(name = "name")
    public void setName(View view, String value) {
      TableView tableView =(TableView) view;
      tableView.setName(value);
    }

    @ReactProp(name = "headerContentStyle")
    public void setHeaderContentStyle(View view, ReadableMap source) {
      HeaderContentStyle headerContentStyle = new HeaderContentStyle(source);
      TableView tableView = (TableView) view;
      tableView.setHeaderStyle(headerContentStyle);
      
      // Try to initialize if all required props are present
      if(tableView.isInitialized() && 
         tableView.cellContentStyle != null && 
         tableView.headerContentStyle != null) {
        tableView.initialize();
      }
    }

    @ReactProp(name = "cellContentStyle")
    public void setCellContentStyle(View view, ReadableMap source) {
      CellContentStyle cellContentStyle = new CellContentStyle(source);
      TableView tableView = (TableView) view;
      tableView.setCellContentStyle(cellContentStyle);
      
      // Try to initialize if all required props are present
      if(tableView.isInitialized() && 
         tableView.cellContentStyle != null && 
         tableView.headerContentStyle != null) {
        tableView.initialize();
      }
    }

  @Nullable
  @Override
  public Map getExportedCustomDirectEventTypeConstants() {
    return MapBuilder.<String, Object>builder()
      .put("onEndReached",
        MapBuilder.of("registrationName", "onEndReached"))
      .put("onSelectionsChanged",
        MapBuilder.of("registrationName", "onSelectionsChanged"))
      .put("onHeaderPressed",
        MapBuilder.of("registrationName", "onHeaderPressed"))
      .put("onExpandCell",
        MapBuilder.of("registrationName", "onExpandCell"))
      .put("onSearchColumn",
        MapBuilder.of("registrationName", "onSearchColumn"))
      .put("onDragBox",
        MapBuilder.of("registrationName", "onDragBox"))
      .build();
  }
}
