<?php
namespace App\Services;

use App\Repositories\FieldConfigRepository;
use App\Repositories\LookupRepository;

/**
 * Field Permission Service
 * Manages field-level access control and validation
 */
class FieldPermissionService
{
    private FieldConfigRepository $fieldConfigRepo;
    private LookupRepository $lookupRepo;

    public function __construct(FieldConfigRepository $fieldConfigRepo, LookupRepository $lookupRepo)
    {
        $this->fieldConfigRepo = $fieldConfigRepo;
        $this->lookupRepo = $lookupRepo;
    }

    /**
     * Map logical field names to alternate names (e.g. lookup group codes)
     */
    private function getFieldNameWithAliases(string $fieldName): array
    {
        $aliases = [
            'payment_method' => ['payment_methods'],
            'destination_bank' => ['payment_destination_banks', 'bank_destination'],
            'payment_card_last4' => ['card_last4', 'card_last_4', 'payment_card_last_4'],
        ];
        
        $result = [];
        // Priority: If an alias exists, check it FIRST (since UI saves as group code)
        if (isset($aliases[$fieldName])) {
            $result = array_merge($result, $aliases[$fieldName]);
        }
        $result[] = $fieldName;
        return $result;
    }

    /**
     * Find config by field name or its aliases
     */
    private function getConfigWithAliases(string $fieldName): ?array
    {
        $names = $this->getFieldNameWithAliases($fieldName);
        foreach ($names as $name) {
            $config = $this->fieldConfigRepo->getByFieldName($name);
            if ($config) return $config;
        }
        return null;
    }

    /**
     * Check if user can edit a specific field
     * ✅ Respects field configuration and user role
     * 
     * @param string $fieldName The field identifier
     * @param string $userRole The user's role
     * @param bool $isExisting Whether the entity already exists (for editable_after_create check)
     */
    public function canUserEditField(string $fieldName, string $userRole, bool $isExisting = false): bool
    {
        // Get field config (check aliases too)
        $config = $this->getConfigWithAliases($fieldName);

        // If field doesn't exist in config, allow by default
        if (!$config) {
            return true;
        }

        // If field is not active, deny
        if (!$config['is_active']) {
            return false;
        }

        // If field is not editable at all, deny
        if (!$config['is_editable']) {
            return false;
        }

        // If entity exists and field is not editable after create, deny
        if ($isExisting && !($config['is_editable_after_create'] ?? true)) {
            return false;
        }

        // Check role permissions
        $allowedRoles = $config['edit_allow_roles'] ?? [];

        // Empty roles = everyone can edit
        if (empty($allowedRoles)) {
            return true;
        }

        // Check if user's role is in allowed roles
        return in_array($userRole, $allowedRoles);
    }

    /**
     * Check if user can delete related to a field/entity
     * For example: can user delete a payment row?
     */
    public function canUserDeleteField(string $fieldName, string $userRole): bool
    {
        $config = $this->getConfigWithAliases($fieldName);

        // If field doesn't exist in config, allow by default
        if (!$config) {
            return true;
        }

        // If field is not active, deny
        if (!$config['is_active']) {
            return false;
        }

        // Check delete role permissions
        $allowedRoles = $config['delete_allow_roles'] ?? [];

        // Empty roles = everyone can delete
        if (empty($allowedRoles)) {
            return true;
        }

        return in_array($userRole, $allowedRoles);
    }

    /**
     * Check if user can view a specific field
     */
    public function canUserViewField(string $fieldName, string $userRole): bool
    {
        $config = $this->getConfigWithAliases($fieldName);

        // If field doesn't exist in config, allow by default
        if (!$config) {
            return true;
        }

        // If field is not active, deny
        if (!$config['is_active']) {
            return false;
        }

        // Check view role permissions
        $allowedRoles = $config['view_allow_roles'] ?? [];

        // Empty roles = everyone can view
        if (empty($allowedRoles)) {
            return true;
        }

        return in_array($userRole, $allowedRoles);
    }

    /**
     * Check if a field is required
     */
    public function isFieldRequired(string $fieldName): bool
    {
        $config = $this->getConfigWithAliases($fieldName);
        return $config ? (bool)$config['is_required'] : false;
    }

    /**
     * Check if a field is active
     */
    public function isFieldActive(string $fieldName): bool
    {
        $config = $this->getConfigWithAliases($fieldName);
        return $config ? (bool)$config['is_active'] : true;
    }

    /**
     * Check if a field is editable after creation
     */
    public function isFieldEditableAfterCreate(string $fieldName): bool
    {
        $config = $this->getConfigWithAliases($fieldName);
        return $config ? (bool)($config['is_editable_after_create'] ?? true) : true;
    }

    /**
     * Get field configuration
     */
    public function getFieldConfig(string $fieldName): ?array
    {
        return $this->getConfigWithAliases($fieldName);
    }

    /**
     * Get the default value for a field
     */
    public function getDefaultValue(string $fieldName): ?string
    {
        $config = $this->getConfigWithAliases($fieldName);
        return $config ? ($config['default_value'] ?? null) : null;
    }

    /**
     * Get the help text for a field
     */
    public function getHelpText(string $fieldName): ?string
    {
        $config = $this->getConfigWithAliases($fieldName);
        return $config ? ($config['help_text'] ?? null) : null;
    }

    /**
     * Check conditional visibility logic
     * Returns true if field should be visible based on conditions
     */
    public function checkConditionalVisibility(string $fieldName, array $formData): bool
    {
        $config = $this->getConfigWithAliases($fieldName);
        
        if (!$config || empty($config['conditional_logic'])) {
            return true; // No conditions = always visible
        }

        $conditions = $config['conditional_logic'];
        
        // Support simple condition format: {"field": "payment_method", "operator": "=", "value": "card_to_card"}
        // Or array of conditions (AND logic)
        if (!is_array($conditions)) {
            return true;
        }

        // Single condition object
        if (isset($conditions['field'])) {
            return $this->evaluateCondition($conditions, $formData);
        }

        // Multiple conditions (AND logic)
        foreach ($conditions as $condition) {
            if (!$this->evaluateCondition($condition, $formData)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Evaluate a single condition
     */
    private function evaluateCondition(array $condition, array $formData): bool
    {
        $field = $condition['field'] ?? '';
        $operator = $condition['operator'] ?? '=';
        $value = $condition['value'] ?? '';

        $actualValue = $formData[$field] ?? '';

        switch ($operator) {
            case '=':
            case '==':
                return $actualValue == $value;
            case '!=':
            case '<>':
                return $actualValue != $value;
            case 'in':
                return is_array($value) && in_array($actualValue, $value);
            case 'not_in':
                return is_array($value) && !in_array($actualValue, $value);
            case 'empty':
                return empty($actualValue);
            case 'not_empty':
                return !empty($actualValue);
            case 'contains':
                return stripos($actualValue, $value) !== false;
            default:
                return true;
        }
    }

    /**
     * Validate field data based on configuration
     * Returns array of validation errors, empty if valid
     */
    public function validateField(string $fieldName, $fieldValue, string $userRole): array
    {
        $errors = [];
        $config = $this->getFieldConfig($fieldName);

        if (!$config) {
            return $errors;
        }

        // Check if field is active
        if (!$config['is_active']) {
            $errors[] = "فیلد {$config['field_label']} غیرفعال است";
            return $errors;
        }

        // Check if field is required
        if ($config['is_required'] && (is_null($fieldValue) || $fieldValue === '')) {
            $errors[] = "{$config['field_label']} الزامی است";
        }

        // Skip further validation if no value
        if (is_null($fieldValue) || $fieldValue === '') {
            return $errors;
        }

        // Check if user can edit this field
        if (!$this->canUserEditField($fieldName, $userRole)) {
            $errors[] = "شما مجاز به ویرایش {$config['field_label']} نیستید";
        }

        // Type-specific validation
        switch ($config['field_type']) {
            case 'number':
                if (!is_numeric($fieldValue)) {
                    $errors[] = "{$config['field_label']} باید عدد باشد";
                }
                break;

            case 'email':
                if (!filter_var($fieldValue, FILTER_VALIDATE_EMAIL)) {
                    $errors[] = "{$config['field_label']} ایمیل معتبر نیست";
                }
                break;

            case 'select':
                // Validate against lookup items if lookup group is specified
                if ($config['lookup_group_code']) {
                    if (!$this->isValidLookupValue($config['lookup_group_code'], $fieldValue)) {
                        $errors[] = "مقدار نامعتبر برای {$config['field_label']}";
                    }
                }
                break;

            case 'text':
                // Basic text validation
                if (strlen($fieldValue) > 255) {
                    $errors[] = "{$config['field_label']} نمی‌تواند بیشتر از ۲۵۵ کاراکتر باشد";
                }
                break;
        }

        return $errors;
    }

    /**
     * Check if a value is valid in a lookup group
     */
    private function isValidLookupValue(string $lookupGroupCode, string $value): bool
    {
        // Get lookup items by group code
        $items = $this->lookupRepo->getItemsByGroupCode($lookupGroupCode, false);

        // Check if value matches any item's code, value, or title
        foreach ($items as $item) {
            $candidates = [
                $item['code'] ?? null,
                $item['value'] ?? null,
                $item['title'] ?? null,
            ];
            foreach ($candidates as $candidate) {
                if ($candidate !== null && $candidate !== '' && $candidate === $value) {
                    return true;
                }
            }
        }

        // Also accept value if it matches by string casting (defensive)
        foreach ($items as $item) {
            $itemValue = (string)($item['value'] ?? $item['code'] ?? $item['title'] ?? '');
            if ($itemValue !== '' && (string)$value === $itemValue) {
                return true;
            }
        }

        return false;
    }

    /**
     * Filter fields by user permission for an entity type
     */
    public function filterEditableFields(array $fields, string $entityType, string $userRole): array
    {
        $configs = $this->fieldConfigRepo->getByEntity($entityType);
        $filtered = [];

        foreach ($fields as $fieldName => $fieldValue) {
            // Check if user can edit this field
            if ($this->canUserEditField($fieldName, $userRole)) {
                $filtered[$fieldName] = $fieldValue;
            }
        }

        return $filtered;
    }

    /**
     * Validate all fields for an entity
     * Returns ['valid' => bool, 'errors' => array]
     */
    public function validateFieldsForEntity(array $data, string $entityType, string $userRole): array
    {
        $errors = [];
        $configs = $this->fieldConfigRepo->getByEntity($entityType, true);

        foreach ($configs as $config) {
            $fieldName = $config['field_name'];
            $fieldValue = $data[$fieldName] ?? null;

            // Validate the field
            $fieldErrors = $this->validateField($fieldName, $fieldValue, $userRole);
            
            if (!empty($fieldErrors)) {
                $errors[$fieldName] = $fieldErrors;
            }
        }

        return [
            'valid' => empty($errors),
            'errors' => $errors
        ];
    }

    /**
     * Get all editable fields for user and entity
     */
    public function getEditableFieldsForEntity(string $entityType, string $userRole): array
    {
        $editable = $this->fieldConfigRepo->getEditableByRole($entityType, $userRole);
        $result = [];

        foreach ($editable as $config) {
            $result[$config['field_name']] = [
                'label' => $config['field_label'],
                'type' => $config['field_type'],
                'required' => (bool)$config['is_required'],
                'editable' => (bool)$config['is_editable'],
                'active' => (bool)$config['is_active'],
                'lookupGroup' => $config['lookup_group_code']
            ];
        }

        return $result;
    }
}

