<?php

namespace App\Http\Controllers;

use App\Mail\SessionReportMail;
use App\Models\MachinePause;
use App\Models\MachineSession;
use App\Models\SessionSegment;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Mail;

class MachineController extends Controller
{
    const FILLING_RATES = [
        'Aima 2ltr' => 250,
        'Aima 1ltr' => 150,
        'Aima 500ml' => 150,
        'Electorade Org 300ml' => 250,
        'Electorade LL 300ml' => 250,
        'Preform 48g (2ltr)' => 1000,
        'Preform 24g (1 ltr)' => 1200,
        'Preform 22g (300mm)' => 1700,
        'Preform 16g (500mm)' => 1800,
    ];

    public function index()
    {
        $activeSession = MachineSession::where('created_by', auth()->id())
            ->whereIn('status', ['running', 'paused'])
            ->with(['pauses', 'activeSegment', 'segments'])
            ->latest()
            ->first();

        return view('operator.dashboard', compact('activeSession'));
    }

    public function saveProduction(Request $request)
    {
        $validated = $request->validate([
            'product_name' => ['required', 'string', 'max:255'],
            'incharge_name' => ['required', 'string', 'max:255'],
            'case_number' => ['nullable', 'string', 'max:255'],
        ]);

        session([
            'production' => [
                'product_name' => $validated['product_name'],
                'incharge_name' => $validated['incharge_name'],
                'case_number' => $validated['case_number'] ?? '',
            ]
        ]);

        return response()->json([
            'success' => true,
            'data' => session('production'),
            'filling_rate' => self::FILLING_RATES[$validated['product_name']] ?? 0,
        ]);
    }

    public function saveCaseNumber(Request $request)
    {
        $validated = $request->validate([
            'case_number' => ['required', 'integer'],
        ]);

        $addedValue = (int) $validated['case_number'];

        if ($addedValue === 0) {
            return response()->json(['success' => false, 'message' => 'Value cannot be zero.'], 422);
        }

        $activeSession = MachineSession::where('created_by', auth()->id())
            ->whereIn('status', ['running', 'paused'])
            ->latest()
            ->first();

        $oee = 0;
        $totalCaseNumber = max(0, $addedValue);
        $history = (string) $addedValue;
        $segmentCaseNumber = max(0, $addedValue);
        $segmentHistory = (string) $addedValue;

        if ($activeSession) {
            // Guard: total cannot go below 0
            $currentTotal = (int) $activeSession->case_number;
            $totalCaseNumber = max(0, $currentTotal + $addedValue);

            if ($addedValue < 0 && $totalCaseNumber === 0 && $currentTotal === 0) {
                return response()->json(['success' => false, 'message' => 'Case count is already 0.'], 422);
            }

            // Build history string: positive shows as "+N", negative shows as "-N"
            $historyPart = $addedValue > 0 ? '+' . $addedValue : (string) $addedValue;
            $currentHistory = $activeSession->case_number_history;
            $history = $currentHistory ? $currentHistory . $historyPart : (string) $addedValue;

            $activeSession->update([
                'case_number' => $totalCaseNumber,
                'case_number_history' => $history,
            ]);

            // Update active segment
            $segment = SessionSegment::where('machine_session_id', $activeSession->id)
                ->where('is_active', true)
                ->first();

            if ($segment) {
                $segmentCaseNumber = max(0, (int) $segment->case_number + $addedValue);
                $segHistoryPart = $addedValue > 0 ? '+' . $addedValue : (string) $addedValue;
                $segmentHistory = $segment->case_number_history
                    ? $segment->case_number_history . $segHistoryPart
                    : (string) $addedValue;

                $segment->update([
                    'case_number' => $segmentCaseNumber,
                    'case_number_history' => $segmentHistory,
                ]);
            }

            $oee = $this->calculateOee($activeSession->fresh());
        }

        $production = session('production', []);
        $production['case_number'] = (string) $totalCaseNumber;
        session(['production' => $production]);

        return response()->json([
            'success' => true,
            'case_number' => $totalCaseNumber,
            'case_number_history' => $history,
            'added' => $addedValue,
            'oee' => $oee,
            'segment_case_number' => $segmentCaseNumber,
            'segment_case_number_history' => $segmentHistory,
        ]);
    }

    public function run(Request $request)
    {
        $production = session('production');

        if (!$production || empty($production['product_name'])) {
            return response()->json([
                'success' => false,
                'message' => 'Please fill Production Entry first.',
            ], 422);
        }

        $existing = MachineSession::where('created_by', auth()->id())
            ->whereIn('status', ['running', 'paused'])
            ->first();

        if ($existing) {
            return response()->json([
                'success' => false,
                'message' => 'A session is already running.',
            ], 422);
        }

        $fillingRate = self::FILLING_RATES[$production['product_name']] ?? 0;
        $now = Carbon::now();

        $session = MachineSession::create([
            'product_name' => $production['product_name'],
            'incharge_name' => $production['incharge_name'],
            'case_number' => $production['case_number'] ?? null,
            'start_time' => $now,
            'status' => 'running',
            'filling_rate' => $fillingRate,
            'created_by' => auth()->id(),
        ]);

        // Create the first segment
        $segment = SessionSegment::create([
            'machine_session_id' => $session->id,
            'product_name' => $production['product_name'],
            'filling_rate' => $fillingRate,
            'case_number' => 0,
            'started_at' => $now,
            'is_active' => true,
        ]);

        return response()->json([
            'success' => true,
            'session' => $session->load(['segments', 'activeSegment']),
            'start_time' => $session->start_time->toIso8601String(),
            'filling_rate' => $fillingRate,
            'active_segment' => $segment,
        ]);
    }

    public function changeProduct(Request $request)
    {
        $validated = $request->validate([
            'product_name' => ['required', 'string', 'max:255'],
        ]);

        $newProduct = $validated['product_name'];
        $newRate = self::FILLING_RATES[$newProduct] ?? 0;

        $session = MachineSession::where('created_by', auth()->id())
            ->whereIn('status', ['running', 'paused'])
            ->latest()
            ->first();

        if (!$session) {
            return response()->json([
                'success' => false,
                'message' => 'No active session found.',
            ], 422);
        }

        // Close current active segment
        $currentSegment = SessionSegment::where('machine_session_id', $session->id)
            ->where('is_active', true)
            ->first();

        $now = Carbon::now();

        if ($currentSegment) {
            $runSeconds = $this->computeSegmentRunSeconds($currentSegment, $now);
            $currentSegment->update([
                'ended_at' => $now,
                'run_seconds' => $runSeconds,
                'is_active' => false,
            ]);
        }

        // Create new segment
        $newSegment = SessionSegment::create([
            'machine_session_id' => $session->id,
            'product_name' => $newProduct,
            'filling_rate' => $newRate,
            'case_number' => 0,
            'started_at' => $now,
            'is_active' => true,
        ]);

        // Update session-level product name and filling rate
        $session->update([
            'product_name' => $newProduct,
            'filling_rate' => $newRate,
        ]);

        // Update PHP session
        $production = session('production', []);
        $production['product_name'] = $newProduct;
        session(['production' => $production]);

        return response()->json([
            'success' => true,
            'segment' => $newSegment,
            'filling_rate' => $newRate,
            'product_name' => $newProduct,
            'segments' => $session->fresh()->segments,
        ]);
    }

    public function pause(Request $request)
    {
        $validated = $request->validate([
            'pause_reason' => ['required', 'string', 'max:1000'],
        ]);

        $session = MachineSession::where('created_by', auth()->id())
            ->where('status', 'running')
            ->latest()
            ->first();

        if (!$session) {
            return response()->json([
                'success' => false,
                'message' => 'No running session found.',
            ], 422);
        }

        $pauseTime = Carbon::now();

        $activeSegment = $session->segments()->where('is_active', true)->first();

        MachinePause::create([
            'machine_session_id'  => $session->id,
            'pause_time'          => $pauseTime,
            'pause_reason'        => $validated['pause_reason'],
            'case_number_at_pause' => $activeSegment ? ($activeSegment->case_number ?? 0) : 0,
        ]);

        $session->update(['status' => 'paused']);

        return response()->json([
            'success' => true,
            'session' => $session->fresh()->load(['pauses', 'segments']),
            'pause_time' => $pauseTime->toIso8601String(),
        ]);
    }

    public function resume(Request $request)
    {
        $session = MachineSession::where('created_by', auth()->id())
            ->where('status', 'paused')
            ->latest()
            ->first();

        if (!$session) {
            return response()->json([
                'success' => false,
                'message' => 'No paused session found.',
            ], 422);
        }

        $openPause = MachinePause::where('machine_session_id', $session->id)
            ->whereNull('resume_time')
            ->latest()
            ->first();

        $resumeTime = Carbon::now();

        if ($openPause) {
            $pauseDuration = $resumeTime->diffInSeconds($openPause->pause_time);
            $openPause->update([
                'resume_time' => $resumeTime,
                'duration' => $pauseDuration,
            ]);

            $session->update([
                'status' => 'running',
                'total_pause_duration' => $session->total_pause_duration + $pauseDuration,
            ]);
        } else {
            $session->update(['status' => 'running']);
        }

        return response()->json([
            'success' => true,
            'session' => $session->fresh()->load(['pauses', 'segments']),
            'resume_time' => $resumeTime->toIso8601String(),
        ]);
    }

    public function stop(Request $request)
    {
        $validated = $request->validate([
            'stop_reason' => ['nullable', 'string', 'max:1000'],
        ]);

        $session = MachineSession::where('created_by', auth()->id())
            ->whereIn('status', ['running', 'paused'])
            ->latest()
            ->first();

        if (!$session) {
            return response()->json([
                'success' => false,
                'message' => 'No running session found.',
            ], 422);
        }

        $stopTime = Carbon::now();

        // Auto-close open pause if paused
        $openPause = MachinePause::where('machine_session_id', $session->id)
            ->whereNull('resume_time')
            ->latest()
            ->first();

        if ($openPause) {
            $pauseDuration = $stopTime->diffInSeconds($openPause->pause_time);
            $openPause->update([
                'resume_time' => $stopTime,
                'duration' => $pauseDuration,
            ]);
            $session->total_pause_duration += $pauseDuration;
        }

        // Close active segment
        $activeSegment = SessionSegment::where('machine_session_id', $session->id)
            ->where('is_active', true)
            ->first();

        if ($activeSegment) {
            $activeSegment->update([
                'ended_at' => $stopTime,
                'run_seconds' => $this->computeSegmentRunSeconds($activeSegment, $stopTime),
                'is_active' => false,
            ]);
        }

        $totalDuration = $stopTime->diffInSeconds($session->start_time);

        $session->update([
            'stop_time' => $stopTime,
            'total_duration' => $totalDuration,
            'total_pause_duration' => $session->total_pause_duration,
            'stop_reason' => $validated['stop_reason'],
            'status' => 'stopped',
        ]);

        $actualRunDuration = $totalDuration - $session->total_pause_duration;

        // Compute final OEE across all segments
        $freshSession = $session->fresh()->load(['pauses', 'segments']);
        [$oee] = $this->buildReportData($freshSession);

        return response()->json([
            'success' => true,
            'session' => $freshSession,
            'oee' => $oee,
            'duration_formatted' => gmdate('H:i:s', $actualRunDuration),
            'total_formatted' => gmdate('H:i:s', $totalDuration),
            'pause_formatted' => gmdate('H:i:s', $session->total_pause_duration),
        ]);
    }

    public function status()
    {
        $session = MachineSession::where('created_by', auth()->id())
            ->whereIn('status', ['running', 'paused'])
            ->with(['pauses', 'activeSegment'])
            ->latest()
            ->first();

        if (!$session) {
            return response()->json(['running' => false]);
        }

        return response()->json([
            'running' => $session->status === 'running',
            'paused' => $session->status === 'paused',
            'session' => $session,
            'start_time' => $session->start_time->toIso8601String(),
            'oee' => $this->calculateOee($session),
            'active_segment' => $session->activeSegment,
        ]);
    }

    public function exportPdf(MachineSession $session)
    {
        // Ensure operator owns this session
        abort_if($session->created_by !== auth()->id(), 403);

        $session->load(['operator', 'pauses', 'segments']);

        [$oee, $segmentOees, $timeline] = $this->buildReportData($session);

        $pdf = Pdf::loadView('admin.sessions.pdf', compact('session', 'oee', 'segmentOees', 'timeline'))
                  ->setPaper('a4', 'portrait');

        $filename = 'shift-report-' . $session->id . '-' . now()->format('Ymd') . '.pdf';
        return $pdf->download($filename);
    }

    public function sendEmail(MachineSession $session, Request $request)
    {
        // Ensure operator owns this session
        abort_if($session->created_by !== auth()->id(), 403);

        $validated = $request->validate([
            'emails' => ['required', 'string'],
        ]);

        // Parse comma-separated emails
        $emails = array_filter(array_map('trim', explode(',', $validated['emails'])));
        $emails = array_values(array_unique($emails));

        // Basic email validation
        foreach ($emails as $email) {
            if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                return response()->json(['success' => false, 'message' => "Invalid email: $email"], 422);
            }
        }

        if (empty($emails)) {
            return response()->json(['success' => false, 'message' => 'Please provide at least one email address.'], 422);
        }

        $session->load(['operator', 'pauses', 'segments']);
        [$oee, $segmentOees, $timeline] = $this->buildReportData($session);

        try {
            foreach ($emails as $email) {
                Mail::to($email)->send(new SessionReportMail($session, $oee, $segmentOees, $timeline));
            }
            return response()->json([
                'success' => true,
                'message' => 'Report sent to ' . implode(', ', $emails),
            ]);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => 'Failed to send: ' . $e->getMessage()], 500);
        }
    }

    private function buildReportData(MachineSession $session): array
    {
        $segmentOees = [];
        $totalExpected = 0;
        $totalCases = 0;

        foreach ($session->segments as $segment) {
            $segmentOees[$segment->id] = $segment->calculateOee();
            $runHours = $segment->run_seconds / 3600;
            $totalExpected += $runHours * $segment->filling_rate;
            $totalCases += (int) $segment->case_number;
        }

        $oee = 0;
        if ($session->segments->count() > 0 && $totalExpected > 0 && $totalCases > 0) {
            $oee = round(($totalCases / $totalExpected) * 100, 1);
        } elseif ($session->segments->count() === 0) {
            $actualRunSeconds = 0;
            if ($session->status === 'stopped' && $session->total_duration > 0) {
                $actualRunSeconds = $session->total_duration - $session->total_pause_duration;
            }
            $runHours = $actualRunSeconds / 3600;
            $cases = (int) $session->case_number;
            $expected = $runHours * $session->filling_rate;
            if ($cases > 0 && $expected > 0) {
                $oee = round(($cases / $expected) * 100, 1);
            }
        }

        // Build timeline
        $timeline = [];
        $firstSegment = $session->segments->first();
        $timeline[] = [
            'type' => 'start',
            'time' => $session->start_time,
            'product' => $firstSegment ? $firstSegment->product_name : $session->product_name,
            'filling_rate' => $firstSegment ? $firstSegment->filling_rate : $session->filling_rate,
        ];
        foreach ($session->segments as $index => $segment) {
            if ($index === 0) continue;
            $timeline[] = [
                'type' => 'product_change',
                'time' => $segment->started_at,
                'product' => $segment->product_name,
                'filling_rate' => $segment->filling_rate,
            ];
        }
        foreach ($session->pauses as $pause) {
            $timeline[] = ['type' => 'pause', 'time' => $pause->pause_time, 'reason' => $pause->pause_reason];
            if ($pause->resume_time) {
                $timeline[] = ['type' => 'resume', 'time' => $pause->resume_time, 'duration' => $pause->duration];
            }
        }
        if ($session->status === 'stopped' && $session->stop_time) {
            $timeline[] = ['type' => 'stop', 'time' => $session->stop_time, 'reason' => $session->stop_reason];
        }
        usort($timeline, fn($a, $b) => $a['time']->timestamp <=> $b['time']->timestamp);

        return [$oee, $segmentOees, $timeline];
    }

    private function calculateOee(MachineSession $session): float
    {
        $caseNumber = (int) $session->case_number;

        if ($session->filling_rate <= 0 || $caseNumber <= 0) {
            return 0;
        }

        $elapsed = now()->diffInSeconds($session->start_time);
        $actualRunSeconds = $elapsed - $session->total_pause_duration;

        if ($actualRunSeconds <= 0) {
            return 0;
        }

        $actualRunHours = $actualRunSeconds / 3600;
        $expectedOutput = $actualRunHours * $session->filling_rate;

        if ($expectedOutput <= 0) {
            return 0;
        }

        return round(($caseNumber / $expectedOutput) * 100, 1);
    }

    private function computeSegmentRunSeconds(SessionSegment $segment, Carbon $endTime): int
    {
        $segStart = $segment->started_at;
        $wallSeconds = $endTime->diffInSeconds($segStart);

        // Sum pause durations that overlap with this segment's time window
        $pauses = MachinePause::where('machine_session_id', $segment->machine_session_id)->get();
        $pauseInSegment = 0;

        foreach ($pauses as $pause) {
            $pStart = $pause->pause_time;
            $pEnd = $pause->resume_time ?? $endTime;

            // Clamp to segment boundaries
            $overlapStart = max($pStart->timestamp, $segStart->timestamp);
            $overlapEnd = min($pEnd->timestamp, $endTime->timestamp);

            if ($overlapEnd > $overlapStart) {
                $pauseInSegment += ($overlapEnd - $overlapStart);
            }
        }

        return max(0, $wallSeconds - $pauseInSegment);
    }
}
