import json
import traceback
import base64
import numpy as np
import pandas as pd
from rest_framework.views import APIView
from rest_framework import viewsets
from DepositAPI.api.Response import SuccessResponse, ErrorResponse
from DepositAPI.common import Utility
from DepositAPI.common.Decorator import view_api, require_jwt_authent


class BaseApiView(viewsets.ViewSet):
    params = None
    sortBy = None
    filter = None
    quickFilter = None
    is_master = None
    user_id = None
    role_id = None
    descending = "DESC"
    searchTerm = ""
    page = 1
    rowsPerPage = 10000000
    offset = 0
    modelClass = None
    MAPPING_FIELDS = {
        'ID': 'ID',
        'Username': 'Username',
        'FullName': 'FullName',
        'UserCode': 'UserCode',
        'PhoneNumber': 'PhoneNumber',
        'Address': 'Address',
    }
    @require_jwt_authent
    def list(self, request):
        result = self.aggregate_data(request)
        return result
    @require_jwt_authent
    def retrieve(self, request, pk=None):
        result = self.get_item_detail(request, pk)
        return result
    @require_jwt_authent
    def edit(self, request, pk=None):
        Utility.del_prop(request, 'CreatedDate')
        Utility.del_prop(request, 'ModifiedDate')
        result = self.edit_data(request, pk)
        return result
    @require_jwt_authent
    def add(self, request):
        Utility.del_prop(request, 'CreatedDate')
        Utility.del_prop(request, 'ModifiedDate')
        result = self.add_entry(request)
        return result
    @require_jwt_authent
    def remove(self, request, pk=None):
        result = self.remove_entry(request, pk)
        return result

    def get_common_query_string(self, request):
        if not request.user.is_anonymous:
            self.user_id = request.user.pk
            self.role_id = request.user.roleid

        self.sortBy = self.params.get("sortBy")
        if self.sortBy == None:
            self.sortBy = "ID"
        param = self.params.get("descending")
        if param == "false":
            self.descending = "ASC"
        
        quickFilter_param = self.params.get("q")
        if quickFilter_param:
            # Robustly handle base64 input that may have missing padding or already be plain JSON
            try:
                s = quickFilter_param
                # Add padding if necessary
                s_padded = s + '=' * (-len(s) % 4)
                decoded = base64.b64decode(s_padded)
                decoded_str = decoded.decode('utf-8') if isinstance(decoded, (bytes, bytearray)) else decoded
                self.quickFilter = json.loads(decoded_str).get("filter", [])
            except Exception:
                try:
                    self.quickFilter = json.loads(quickFilter_param).get("filter", [])
                except Exception as e:
                    print(f"Warning: failed to parse quickFilter param: {e}")
                    self.quickFilter = []

        filter_param = self.params.get("filter")
        if filter_param:
            try:
                s = filter_param
                s_padded = s + '=' * (-len(s) % 4)
                decoded = base64.b64decode(s_padded)
                decoded_str = decoded.decode('utf-8') if isinstance(decoded, (bytes, bytearray)) else decoded
                self.filter = json.loads(decoded_str).get("filter", [])
            except Exception:
                try:
                    self.filter = json.loads(filter_param).get("filter", [])
                except Exception as e:
                    print(f"Warning: failed to parse filter param: {e}")
                    self.filter = []

        param = self.params.get("page")
        if param is not None and param.isdigit():
            self.page = int(param)

        param = self.params.get("rowsPerPage")
        if param is not None and param.isdigit():
            self.rowsPerPage = int(param)
            
        self.offset = (self.page - 1) * self.rowsPerPage

        param = self.params.get("fields")
        self.fields = []
        if param is not None and param != '':
            tmp = param.split(',')
            for t in tmp:
                if t in self.MAPPING_FIELDS:
                    self.fields.append(t)
        
        param = self.params.get("is_master")
        if param is not None and param.isdigit():
            self.is_master =int(param)

    def get_body(self, request, body=None):
        return request.data if not body else body

    def get_params(self, request):
        return request.query_params

    def get_request_info(self, request):
        self.params = self.get_params(request)

    def aggregate_data(self, request):
        if self.modelClass != None:
            self.get_request_info(request)
            self.get_common_query_string(request)
            items, totalRow = self.modelClass.get_list(self.filter, self.quickFilter, self.sortBy, self.descending, self.rowsPerPage, self.offset)
            return SuccessResponse(data=items, total=totalRow, offset=self.offset, rowsPerPage=self.rowsPerPage)
        return ErrorResponse(error="Please implement the API!")

    def get_item_detail(self, request, pk):
        if self.modelClass != None:
            self.get_request_info(request)
            item = self.modelClass.get_detail(pk)
            return SuccessResponse(data=item)
        return ErrorResponse(error="Please implement the API!")

    def edit_data(self, request, pk):
        if self.modelClass != None:
            ret = self.modelClass.update(request, pk)
            return SuccessResponse(data=ret)
        return ErrorResponse(error="Please implement the API!")

    def add_entry(self, request):
        if self.modelClass != None:
            item = self.modelClass.insert(request)
            return SuccessResponse( data=item )
        return ErrorResponse(error="Please implement the API!")

    def remove_entry(self, request, pk):
        if self.modelClass != None:
            self.modelClass.remove(request, pk)
            return SuccessResponse( data={"status": True} )
        return ErrorResponse(error="Please implement the API!")
