<?php
if (!defined('ABSPATH')) {
    exit;
}

class Novajyin_Rules_Engine
{
    public static function apply($context)
    {
        $rules = Novajyin_Repository::get_rules(true);
        $updates = array();
        $notifications = array();
        $matched_rules = array();

        foreach ($rules as $rule) {
            $conditions = Novajyin_Repository::decode_json($rule->conditions_json);
            $actions = Novajyin_Repository::decode_json($rule->actions_json);

            if (!self::matches_all($conditions, $context)) {
                continue;
            }

            $matched_rules[] = $rule->rule_name;

            foreach (array('assignee_user_id', 'assignee_group_id', 'status_id', 'priority_id') as $key) {
                if (isset($actions[$key]) && absint($actions[$key]) > 0) {
                    $updates[$key] = absint($actions[$key]);
                }
            }

            if (!empty($actions['due_type'])) {
                $due_date = self::calculate_due_date($actions['due_type'], $actions['due_value'] ?? '');
                if ($due_date) {
                    $updates['due_date'] = $due_date;
                }
            }

            if (!empty($actions['notify_emails'])) {
                $emails = self::parse_emails($actions['notify_emails']);
                $notifications = array_merge($notifications, $emails);
            }
        }

        return array(
            'updates' => $updates,
            'notifications' => array_values(array_unique(array_filter($notifications))),
            'matched_rules' => $matched_rules,
        );
    }

    private static function matches_all($conditions, $context)
    {
        if (empty($conditions)) {
            return false;
        }

        foreach ($conditions as $condition) {
            if (!self::matches_condition($condition, $context)) {
                return false;
            }
        }

        return true;
    }

    private static function matches_condition($condition, $context)
    {
        $field = sanitize_text_field($condition['field'] ?? '');
        $operator = sanitize_key($condition['operator'] ?? 'equals');
        $expected = isset($condition['value']) ? (string) $condition['value'] : '';
        $actual = self::field_value($field, $context);

        if (is_array($actual)) {
            $actual = implode(', ', array_map('strval', $actual));
        }

        $actual = (string) $actual;

        switch ($operator) {
            case 'not_equals':
                return $actual !== $expected;
            case 'contains':
                return $expected === '' ? true : self::contains($actual, $expected);
            case 'not_contains':
                return $expected === '' ? true : !self::contains($actual, $expected);
            case 'empty':
                return trim($actual) === '';
            case 'not_empty':
                return trim($actual) !== '';
            case 'equals':
            default:
                return $actual === $expected;
        }
    }

    private static function field_value($field, $context)
    {
        if ($field === '') {
            return '';
        }

        if (strpos($field, 'raw:') === 0) {
            $raw_key = substr($field, 4);
            return self::raw_value($raw_key, $context);
        }

        if (isset($context[$field])) {
            return $context[$field];
        }

        return self::raw_value($field, $context);
    }

    private static function contains($actual, $expected)
    {
        if (function_exists('mb_stripos')) {
            return mb_stripos($actual, $expected) !== false;
        }

        return stripos($actual, $expected) !== false;
    }

    private static function raw_value($key, $context)
    {
        if ($key === '' || empty($context['raw_data']) || !is_array($context['raw_data'])) {
            return '';
        }

        return $context['raw_data'][$key] ?? '';
    }

    public static function calculate_due_date($due_type, $due_value)
    {
        $timestamp = current_time('timestamp');
        $value = max(1, absint($due_value));

        switch ($due_type) {
            case 'end_of_day':
                return wp_date('Y-m-d 23:59:59', $timestamp);
            case 'hours':
                return wp_date('Y-m-d H:i:s', $timestamp + ($value * HOUR_IN_SECONDS));
            case 'days':
                return wp_date('Y-m-d H:i:s', $timestamp + ($value * DAY_IN_SECONDS));
            case 'business_days':
                return self::add_business_days($timestamp, $value);
            case 'none':
            default:
                return null;
        }
    }

    private static function add_business_days($timestamp, $days)
    {
        $added = 0;
        $current = $timestamp;

        while ($added < $days) {
            $current += DAY_IN_SECONDS;
            $weekday = (int) wp_date('N', $current);
            if ($weekday >= 1 && $weekday <= 5) {
                $added++;
            }
        }

        return wp_date('Y-m-d H:i:s', $current);
    }

    public static function parse_emails($value)
    {
        $emails = preg_split('/[\s,;]+/', (string) $value);
        $clean = array();

        foreach ($emails as $email) {
            $email = sanitize_email($email);
            if ($email && is_email($email)) {
                $clean[] = $email;
            }
        }

        return $clean;
    }

    public static function operator_options()
    {
        return array(
            'equals' => '一致する',
            'not_equals' => '一致しない',
            'contains' => '含む',
            'not_contains' => '含まない',
            'empty' => '空である',
            'not_empty' => '空でない',
        );
    }
}





