<?php

namespace App\Services;

use App\Models\Requirement;
use App\Models\NonFunctionalRequirement;
use App\Models\RequirementOperation;
use App\Models\User;
use App\Models\UserOperationFinal;

class IUSPACalculator
{
    private $alpha = 0.5;
    private $beta = 0.5;
    private $stakeholderWeights = [];

    public function __construct()
    {
        $this->loadStakeholderWeights();
    }

    /**
     * Calculate final weights for a specific project
     */
    public function calculateFinalWeightsForProject(string $projectId)
    {
        $results = [];

        // Step 7: Calculate NFRs ranking with respect to all FUs using TFN and alpha cut
        $nfrResults = $this->calculateNFRRankingForProject($projectId);
        $results['step7'] = $nfrResults;

        // Step 8: Calculate FUs Ranking
        $fuResults = $this->calculateFURankingForProject($projectId, $nfrResults['stakeholder_nw']);
        $results['step8'] = $fuResults;

        // Step 9: Aggregate prioritised lists from various stakeholders
        $finalResults = $this->aggregateStakeholderResults($fuResults, $nfrResults);
        $results['step9'] = $finalResults;

        return $results;
    }

    /**
     * Step 7: Calculate NFRs ranking for a specific project
     */
    private function calculateNFRRankingForProject(string $projectId)
    {
        $userIds = array_keys($this->stakeholderWeights);
        $users = User::whereIn('id', $userIds)->get();
        
        $stakeholderMatrices = [];
        $stakeholderTFN = [];
        $stakeholderW = [];
        $stakeholderNW = [];

        foreach ($users as $user) {
            // Sub-step 7.1: Convert to numerical matrix for this project
            $matrix = $this->buildDecisionMatrixForProject($user, $projectId);
            if (empty($matrix)) {
                continue;
            }
            
            $stakeholderMatrices[$user->id] = $matrix;

            // Sub-step 7.2: Set up triangular fuzzy numbers (TFNs)
            $tfnMatrix = $this->calculateTFN($matrix);
            $stakeholderTFN[$user->id] = $tfnMatrix;

            // Sub-step 7.3 & 7.4: Construct fuzzy priority vector and defuzzification
            list($wVector, $nwVector) = $this->defuzzifyTFN($tfnMatrix);
            $stakeholderW[$user->id] = $wVector;
            $stakeholderNW[$user->id] = $nwVector;
        }

        return [
            'stakeholder_matrices' => $stakeholderMatrices,
            'stakeholder_tfn' => $stakeholderTFN,
            'stakeholder_w' => $stakeholderW,
            'stakeholder_nw' => $stakeholderNW,
            'stakeholder_weights' => $this->stakeholderWeights,
        ];
    }

    /**
     * Build decision matrix for a specific user and project
     */
    private function buildDecisionMatrixForProject($user, string $projectId)
    {
        $matrix = [];
        
        $operations = RequirementOperation::with(['requirement', 'nonFunctionalRequirement'])
            ->where('user_id', $user->id)
            ->where('project_id', $projectId)
            ->where('status', 'done')
            ->get();

        foreach ($operations as $operation) {
            $rId = $operation->r_id;
            $nfrId = $operation->nfu_id;
            $matrix[$rId][$nfrId] = $operation->nfu_value;
        }

        return $matrix;
    }

    /**
     * Step 8: Calculate FUs Ranking for a specific project
     */
    private function calculateFURankingForProject(string $projectId, $stakeholderNW)
    {
        $userIds = array_keys($this->stakeholderWeights);
        $users = User::whereIn('id', $userIds)->get();
        
        $stakeholderR = [];
        $stakeholderNR = [];

        foreach ($users as $user) {
            $matrix = $this->buildDecisionMatrixForProject($user, $projectId);
            if (empty($matrix)) {
                continue;
            }
            
            $nwVector = $stakeholderNW[$user->id] ?? [];
            
            $RVector = [];
            $totalR = 0;

            // Calculate R for each FU (Equation 5)
            foreach ($matrix as $rId => $nfrRatings) {
                $R = 1.0;
                
                foreach ($nfrRatings as $nfrId => $value) {
                    if (isset($nwVector[$nfrId]) && $nwVector[$nfrId] > 0) {
                        $R *= pow($value, $nwVector[$nfrId]);
                    }
                }
                
                $RVector[$rId] = $R;
                $totalR += $R;
            }

            // Normalize R to get NR (Equation 6)
            $NRVector = [];
            foreach ($RVector as $rId => $R) {
                $NRVector[$rId] = $totalR > 0 ? $R / $totalR : 0;
            }

            $stakeholderR[$user->id] = $RVector;
            $stakeholderNR[$user->id] = $NRVector;
        }

        return [
            'stakeholder_r' => $stakeholderR,
            'stakeholder_nr' => $stakeholderNR,
        ];
    }

    // The following methods remain the same as before:
    private function loadStakeholderWeights()
    {
        $weights = UserOperationFinal::with(['userOperation.user'])
            ->get()
            ->groupBy('userOperation.user_id')
            ->map(function ($operations) {
                $latestOperation = $operations->sortByDesc('created_at')->first();
                return $latestOperation->weightage;
            });

        $this->stakeholderWeights = $weights->toArray();

        if (empty($this->stakeholderWeights)) {
            $users = User::limit(4)->get();
            foreach ($users as $index => $user) {
                $this->stakeholderWeights[$user->id] = 1.0 - ($index * 0.1);
            }
        }
    }

    private function calculateTFN($matrix)
    {
        $nfrValues = [];
        
        foreach ($matrix as $rId => $nfrRatings) {
            foreach ($nfrRatings as $nfrId => $value) {
                $nfrValues[$nfrId][] = $value;
            }
        }

        $tfnMatrix = [];
        foreach ($nfrValues as $nfrId => $values) {
            $L = min($values);
            $product = array_product($values);
            $n = count($values);
            $M = $n > 0 ? pow($product, 1/$n) : 0;
            $H = max($values);
            
            $tfnMatrix[$nfrId] = [
                'L' => $L,
                'M' => $M,
                'H' => $H,
                'values' => $values
            ];
        }

        return $tfnMatrix;
    }

    private function defuzzifyTFN($tfnMatrix)
    {
        $wVector = [];
        $nwVector = [];
        $totalW = 0;

        foreach ($tfnMatrix as $nfrId => $tfn) {
            $L = $tfn['L'];
            $M = $tfn['M'];
            $H = $tfn['H'];

            $f_alpha_L = ($M - $L) * $this->alpha + $L;
            $f_alpha_H = $H - ($H - $M) * $this->alpha;
            $W = $this->beta * $f_alpha_L + (1 - $this->beta) * $f_alpha_H;
            
            $wVector[$nfrId] = $W;
            $totalW += $W;
        }

        foreach ($wVector as $nfrId => $W) {
            $nwVector[$nfrId] = $totalW > 0 ? $W / $totalW : 0;
        }

        return [$wVector, $nwVector];
    }

    private function aggregateStakeholderResults($fuResults, $nfrResults)
    {
        $finalUR = $this->aggregateFUs($fuResults['stakeholder_nr']);
        $finalUW = $this->aggregateNFRs($nfrResults['stakeholder_nw']);

        return [
            'final_fu_weights' => $finalUR,
            'final_nfr_weights' => $finalUW,
        ];
    }

    private function aggregateFUs($stakeholderNR)
    {
        $finalUR = [];
        $allRIds = [];
        
        foreach ($stakeholderNR as $userId => $nrVector) {
            $allRIds = array_merge($allRIds, array_keys($nrVector));
        }
        $allRIds = array_unique($allRIds);
        
        $requirements = Requirement::whereIn('id', $allRIds)->get()->keyBy('id');

        foreach ($requirements as $rId => $requirement) {
            $UR = 0;
            foreach ($this->stakeholderWeights as $userId => $weight) {
                $NR = $stakeholderNR[$userId][$rId] ?? 0;
                $UR += $NR * $weight;
            }
            $finalUR[$rId] = $UR;
        }

        arsort($finalUR);
        return $finalUR;
    }

    private function aggregateNFRs($stakeholderNW)
    {
        $finalUW = [];
        $allNfrIds = [];
        
        foreach ($stakeholderNW as $userId => $nwVector) {
            $allNfrIds = array_merge($allNfrIds, array_keys($nwVector));
        }
        $allNfrIds = array_unique($allNfrIds);
        
        $nfrs = NonFunctionalRequirement::whereIn('id', $allNfrIds)->get()->keyBy('id');

        foreach ($nfrs as $nfrId => $nfr) {
            $UW = 0;
            foreach ($this->stakeholderWeights as $userId => $weight) {
                $NW = $stakeholderNW[$userId][$nfrId] ?? 0;
                $UW += $NW * $weight;
            }
            $finalUW[$nfrId] = $UW;
        }

        arsort($finalUW);
        return $finalUW;
    }

    public function updateDatabaseWeights($finalResults)
    {
        foreach ($finalResults['final_fu_weights'] as $rId => $weight) {
            Requirement::where('id', $rId)->update(['weight' => round($weight, 6)]);
        }

        foreach ($finalResults['final_nfr_weights'] as $nfrId => $weight) {
            NonFunctionalRequirement::where('id', $nfrId)->update(['weight' => round($weight, 6)]);
        }
    }

    public function getStakeholderWeights()
    {
        return $this->stakeholderWeights;
    }
}