from typing import Any, Dict, List, Tuple, Optional
from app.core.database.operations import get_items_from_database
from app.models.registry import MODEL_REGISTRY
from datetime import date, datetime


# Mini validation functions
def validate_type(value: Any,
                  expected_type: type) -> Tuple[Any,
                                                Optional[str]]:
    try:
        if expected_type == date:
            if isinstance(value, date):
                return value, None
            elif isinstance(value, str):
                return datetime.strptime(value, "%Y-%m-%d").date(), None
            else:
                return value, 'Pogrešan format datuma'
        return expected_type(value), None
    except (ValueError, TypeError):
        if expected_type in [int, float]:
            return value, 'Morate uneti brojčanu vrednost'
        return value, 'Pogrešan tip podatka'


def validate_max_length(value: str, max_length: int) -> Optional[str]:
    if len(value.strip()) > max_length:
        return f'Dužina ne sme biti veća od {max_length} karaktera'
    return None


def validate_not_empty(value: str, not_empty: bool) -> Optional[str]:
    if not_empty and not value.strip():
        return 'Polje ne sme biti prazno'
    return None


def validate_sign(value: float, sign: str) -> Optional[str]:
    if sign == 'positive' and value < 0:
        return 'Vrednost mora biti pozitivna'
    if sign == 'negative' and value > 0:
        return 'Vrednost mora biti negativna'
    return None


def validate_possible_values(
        value: Any,
        possible_values: List[Any]) -> Optional[str]:
    if value not in possible_values:
        return f'Vrednost mora biti jedna od: {possible_values}'
    return None


# Main validation function
def validate_data(
        form_values: Dict[str, Any], model_name: str) -> Tuple[Dict[str, Any], List[int]]:
    if model_name not in MODEL_REGISTRY:
        return ({'error': f'Model "{model_name}" nije registrovan'}, 400)

    model = MODEL_REGISTRY[model_name]['metadata']['columns']

    errors: Dict[str, str] = {}
    status_codes: List[int] = []
    details: Dict[str, str] = {}

    for field, value in form_values.items():
        field_metadata = model.get(field)

        # Check if the field exists in the model
        if not field_metadata:
            errors[field] = f'Nepoznato polje: {field}'
            status_codes.append(400)
            continue  # Skip this field

        # Ensure constraints exist
        constraints = field_metadata.get('constraints', {})

        # Ensure the type is valid in the model
        field_type = field_metadata.get('type')
        if field_type not in [str, int, float, bool, date]:
            errors[field] = f'Nepoznat tip podatka: {field_type}'
            status_codes.append(500)
            continue

        # Skip validation if the value is None
        if value is None:
            continue

        # Type validation
        if not isinstance(value, field_type):
            casted_value, cast_error = validate_type(
                value=value, expected_type=field_type)
            # print(cast_error)
            if cast_error:
                errors[field] = cast_error
                status_codes.append(422)
                continue
            value = casted_value
            form_values[field] = value  # update after casting

        # String validations
        if field_type == str:
            if 'not_empty' in constraints:
                msg = validate_not_empty(
                    value=value, not_empty=constraints['not_empty'])
                if msg:
                    errors[field] = msg
                    status_codes.append(422)
                    continue
            if 'max_length' in constraints:
                msg = validate_max_length(
                    value=value, max_length=constraints['max_length'])

                if msg:
                    errors[field] = msg
                    status_codes.append(422)
                    continue

        # Numeric validations
        if field_type in [int, float] and 'sign' in constraints:
            msg = validate_sign(value=value, sign=constraints['sign'])
            if msg:
                errors[field] = msg
                status_codes.append(422)
                continue

        # Enum validations
        if 'enum' in constraints:
            enum_class = constraints['enum']
            possible_values = [e.name for e in enum_class]
            msg = validate_possible_values(
                value=value, possible_values=possible_values)
            if msg:
                errors[field] = msg
                status_codes.append(422)
                continue

        # Foreign key validations
        if 'foreign_key' in constraints:
            # Determine if value contains ref table and ref_column
            if isinstance(value, dict) and 'value' in value:
                actual_value = value['value']
                ref_table = value.get(
                    'ref_table', constraints['foreign_key'].get('ref_table'))
                ref_column = value.get(
                    'ref_column', constraints['foreign_key'].get('ref_column'))
            else:
                actual_value = value
                ref_table = constraints['foreign_key'].get('ref_table')
                ref_column = constraints['foreign_key'].get('ref_column')

            # Validate presence of source_table and id_column
            if not ref_table or not ref_column:
                errors[field] = 'Greška pri referenciranju entiteta'
                status_codes.append(500)
                continue

            response = get_items_from_database(table_name=ref_table)
            if 'error' in response:
                errors[field] = response['error']
                if response.get('details'):
                    details[field] = response['details']
                status_codes.append(500)
                continue

            valid_ids = [item[ref_column] for item in response]
            msg = validate_possible_values(
                value=actual_value, possible_values=valid_ids)
            if msg:
                errors[field] = 'Odabran nepostojeci entitet'
                status_codes.append(422)
                continue

    if errors:
        return ({'error': errors, 'details': details}, status_codes)

    return ({}, [200])


# Checks if a dictionary has all the required fields
def check_required_fields_presence(
        input_data: dict, model_name: str) -> Tuple[Dict[str, Any], int]:
    if model_name not in MODEL_REGISTRY:
        return ({'error': f'Model "{model_name}" nije registrovan'}, [400])

    model = MODEL_REGISTRY[model_name]['metadata']['columns']

    missing_fields = []

    for field, meta in model.items():
        is_required = meta.get('required', False)

        if is_required and field not in input_data:
            missing_fields.append(field)

    if missing_fields:
        return ({
            'error': {field: 'Obavezno polje' for field in missing_fields}
        }, [400])

    return ({}, [200])
