<?php

declare(strict_types=1);

namespace common\services;

use common\models\DietPlan;
use common\models\DietPlanMeal;
use common\models\FoodLibrary;
use yii\db\Exception;

/**
 * DietPlanService — handles creation, updating, copying, and nutrition calculation.
 */
class DietPlanService
{
    /**
     * Create a diet plan with meals in a single transaction.
     *
     * @param array $planData   DietPlan fields
     * @param array $mealsData  Array of meal item arrays grouped by meal_type
     * @throws Exception|\Throwable
     */
    public function createPlan(array $planData, array $mealsData = []): DietPlan
    {
        $transaction = \Yii::$app->db->beginTransaction();

        try {
            $plan = new DietPlan();
            $plan->customer_id   = $planData['customer_id'];
            $plan->title         = $planData['title'];
            $plan->description   = $planData['description'] ?? null;
            $plan->start_date    = $planData['start_date'] ?? null;
            $plan->end_date      = $planData['end_date'] ?? null;
            $plan->is_template   = (int)($planData['is_template'] ?? 0);
            $plan->template_name = $planData['template_name'] ?? null;
            $plan->status        = $planData['status'] ?? DietPlan::STATUS_DRAFT;
            $plan->created_by    = \Yii::$app->user->id ?? null;

            if (!$plan->save()) {
                throw new Exception('DietPlan save failed: ' . json_encode($plan->errors));
            }

            $this->saveMeals($plan, $mealsData);

            // Reload meals and recalculate totals
            $plan->refresh();
            $plan->populateRelation('meals', $plan->meals);
            $plan->recalculateTotals();
            $plan->save(false, ['total_calories', 'total_protein', 'total_carbs', 'total_fats']);

            $transaction->commit();
            return $plan;
        } catch (\Throwable $e) {
            $transaction->rollBack();
            throw $e;
        }
    }

    /**
     * Update an existing plan: replace all meals atomically.
     *
     * @throws Exception|\Throwable
     */
    public function updatePlan(DietPlan $plan, array $planData, array $mealsData = []): DietPlan
    {
        $transaction = \Yii::$app->db->beginTransaction();

        try {
            $plan->setAttributes([
                'title'         => $planData['title'] ?? $plan->title,
                'description'   => $planData['description'] ?? $plan->description,
                'start_date'    => $planData['start_date'] ?? $plan->start_date,
                'end_date'      => $planData['end_date'] ?? $plan->end_date,
                'is_template'   => (int)($planData['is_template'] ?? $plan->is_template),
                'template_name' => $planData['template_name'] ?? $plan->template_name,
                'status'        => $planData['status'] ?? $plan->status,
            ]);

            if (!$plan->save()) {
                throw new Exception('DietPlan update failed: ' . json_encode($plan->errors));
            }

            // Delete existing meals and re-insert
            DietPlanMeal::deleteAll(['diet_plan_id' => $plan->id]);
            $this->saveMeals($plan, $mealsData);

            $plan->refresh();
            $plan->recalculateTotals();
            $plan->save(false, ['total_calories', 'total_protein', 'total_carbs', 'total_fats']);

            $transaction->commit();
            return $plan;
        } catch (\Throwable $e) {
            $transaction->rollBack();
            throw $e;
        }
    }

    /**
     * Copy an existing diet plan (optionally to a different customer).
     *
     * @throws Exception|\Throwable
     */
    public function copyPlan(DietPlan $source, int $targetCustomerId, string $newTitle): DietPlan
    {
        // Ensure meals relation is loaded
        $source->populateRelation('meals', $source->getMeals()->all());

        $mealsData = [];
        foreach ($source->meals as $meal) {
            $mealsData[] = [
                'meal_type'       => $meal->meal_type,
                'food_library_id' => $meal->food_library_id,
                'food_name'       => $meal->food_name,
                'quantity'        => $meal->quantity,
                'unit'            => $meal->unit,
                'notes'           => $meal->notes,
                'sort_order'      => $meal->sort_order,
            ];
        }

        return $this->createPlan([
            'customer_id'  => $targetCustomerId,
            'title'        => $newTitle,
            'description'  => $source->description,
            'start_date'   => null,
            'end_date'     => null,
            'is_template'  => 0,
            'status'       => DietPlan::STATUS_DRAFT,
        ], $mealsData);
    }

    /**
     * Insert meal items for a plan. Calculates nutrition from FoodLibrary if food_library_id set.
     */
    private function saveMeals(DietPlan $plan, array $mealsData): void
    {
        if (empty($mealsData)) {
            return;
        }

        // Preload food library items to avoid N+1
        $foodIds = array_filter(array_column($mealsData, 'food_library_id'));
        $foods   = [];
        if (!empty($foodIds)) {
            $foods = FoodLibrary::find()
                ->where(['id' => $foodIds])
                ->indexBy('id')
                ->all();
        }

        $rows = [];
        foreach ($mealsData as $sort => $mealData) {
            $cal  = 0.0;
            $prot = 0.0;
            $carb = 0.0;
            $fat  = 0.0;

            $foodId = $mealData['food_library_id'] ?? null;
            $qty    = (float)($mealData['quantity'] ?? 100);

            if ($foodId && isset($foods[$foodId])) {
                $food = $foods[$foodId];
                // Scale nutrition to quantity (food values are per 100g)
                $factor = $qty / 100;
                $cal    = round((float)$food->calories * $factor, 2);
                $prot   = round((float)$food->protein * $factor, 2);
                $carb   = round((float)$food->carbs * $factor, 2);
                $fat    = round((float)$food->fats * $factor, 2);
            } else {
                $cal  = (float)($mealData['calories'] ?? 0);
                $prot = (float)($mealData['protein'] ?? 0);
                $carb = (float)($mealData['carbs'] ?? 0);
                $fat  = (float)($mealData['fats'] ?? 0);
            }

            $rows[] = [
                $plan->id,
                $mealData['meal_type'],
                $foodId ?: null,
                $foodId ? null : ($mealData['food_name'] ?? null),
                $qty,
                $mealData['unit'] ?? 'g',
                $cal,
                $prot,
                $carb,
                $fat,
                $mealData['notes'] ?? null,
                (int)($mealData['sort_order'] ?? $sort),
            ];
        }

        \Yii::$app->db->createCommand()->batchInsert(
            '{{%diet_plan_meals}}',
            ['diet_plan_id', 'meal_type', 'food_library_id', 'food_name', 'quantity', 'unit', 'calories', 'protein', 'carbs', 'fats', 'notes', 'sort_order'],
            $rows
        )->execute();
    }

    /**
     * Get food suggestions for AJAX autocomplete.
     */
    public function getFoodSuggestions(string $query, ?int $userId, int $limit = 10): array
    {
        return FoodLibrary::findVisibleToUser($userId)
            ->andWhere(['LIKE', 'name', $query])
            ->limit($limit)
            ->asArray()
            ->all();
    }
}
