<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\ApplicationDeviation;
use App\ApplicationDeviationLog;
use Carbon\Carbon;
use App\Comments;
use App\ConsumerApplication;
use App\DeviationType;
use App\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use App\Slack;

class ApplicationDeviationController extends Controller{

    public function saveDeviation(Request $request) {

        $rules = [
            'id' => 'nullable|integer',
            'consumer_application_id' => 'required|integer',
            'application_details' => 'nullable|string',
            'maker_deviation_notes' => 'nullable|string',
            'checker_id' => 'nullable|integer',
            'checker_comment' => 'nullable|string',
            'status' => 'nullable|string',
        ];

        if (!$request->filled('id')) {
            $rules['deviation_type_id'] = 'required|integer';
        }

        $validated = $request->validate($rules);

        if ($request->filled('id')) {
            $deviation = ApplicationDeviation::findOrFail($request->id);

            // capture old values for logging
            $old_status = $deviation->status;
            $old_application_details = $deviation->application_details;
            $old_checker_comment = $deviation->checker_comment ?? '';

            $deviation->fill($validated);

            // Only update status if explicitly provided in the edit form
            if($request->has('status')){
                $deviation->status = $request->status;
            }

            $deviation->save();

            // determine values for log
            $event = 'update';
            $logDetails = $request->filled('application_details') ? $request->application_details : $old_application_details;
            $logStatus = $request->has('status') ? $request->status : $deviation->status;
            $logNotes = $request->filled('maker_deviation_notes') ? $request->maker_deviation_notes : ($deviation->maker_deviation_notes ?? '');
            $logComment = $request->filled('checker_comment') ? $request->checker_comment : $old_checker_comment;

        } else {
            $deviation = new ApplicationDeviation();
            $deviation->fill($validated);
            $deviation->maker_id = Auth::id(); // Maker is set only on creation
            $deviation->status = 'Pending';
            $deviation->save();

            $deviationType = DeviationType::find($request->deviation_type_id);
            $approverIds = [];
            
            if ($deviationType && !empty($deviationType->approver_user_ids)) {
                $approvers = is_string($deviationType->approver_user_ids) 
                    ? json_decode($deviationType->approver_user_ids, true) 
                    : $deviationType->approver_user_ids;

                if (is_array($approvers)) {
                    $approverIds = $approvers;
                }
            } elseif ($request->checker_id) {
                $approverIds = [$request->checker_id];
            }

            if (!empty($approverIds)) {
                $this->sendNotification(
                    $approverIds,
                    $request->consumer_application_id,
                    Auth::id(),
                    $deviationType ? ($deviationType->category ?? 'Credit') : 'Credit'
                );
            }

            $application = ConsumerApplication::find($request->consumer_application_id);
            $this->sendDeviationSlackNotification('create', [
                'category' => $deviationType ? $deviationType->category : 'Credit',
                'deviationName' => $deviationType ? $deviationType->name : 'Unknown',
                'customerName' => $application ? $application->name : 'Unknown',
                'appId' => $request->consumer_application_id,
                'notes' => $request->filled('maker_deviation_notes') ? $request->maker_deviation_notes : null
            ]);

            $old_status = null;
            $event = 'create';
            $logDetails = $request->filled('application_details') ? $request->application_details : $deviation->application_details;
            $logStatus = $request->has('status') ? $request->status : $deviation->status;
            $logNotes = $request->filled('maker_deviation_notes') ? $request->maker_deviation_notes : ($deviation->maker_deviation_notes ?? '');
            $logComment = $request->filled('checker_comment') ? $request->checker_comment : ($deviation->checker_comment ?? '');
        }

        $checker_id_for_log = $request->filled('checker_id') 
            ? $request->checker_id 
            : null;

        $this->logDeviationHistory(
            $deviation->id,
            $event,
            $old_status,
            $logStatus,
            $logDetails,
            $checker_id_for_log,
            $logNotes,
            $logComment
        );

        $deviation->load(['maker', 'checker', 'deviationType']);

        return response()->json(['success' => true, 'deviation' => $deviation]);
    }

    public function updateStatus(Request $request, $id){
        $deviation = ApplicationDeviation::findOrFail($id);

        // capture old values
        $old_status = $deviation->status;
        $old_application_details = $deviation->application_details;
        $old_checker_comment = $deviation->checker_comment ?? '';

        // update fields
        $checker_name = $request->checker_name ?: Auth::user()->name;
        $deviation->status = $request->status;
        if($request->has('checker_comment')){
            $deviation->checker_comment = $request->checker_comment;
        }
        $deviation->save();

        if ($old_status != $request->status) {
            
            // Define variables for the string
            $deviation_on = $request->deviation_name; 
            $comment_text = "Deviation: {$request->status} on {$deviation_on} by {$checker_name}";

            Comments::insert([
                'application_id' => $deviation->consumer_application_id, // Assuming deviation has application_id
                'user_id'        => Auth::id(),
                'comment'        => $comment_text,
                'show_consumer'  => 0,
                'show_merchant'  => 0,
                'created_at'     => Carbon::now()->toDateTimeString(),
                'updated_at'     => Carbon::now()->toDateTimeString(),
                'type'           => 'consumer',
            ]);

            $this->sendStatusUpdateNotification($deviation, $request->status, $checker_name, $request->checker_comment, $deviation_on);
        }

        $logDetails = $request->filled('application_details') ? $request->application_details : $old_application_details;
        $logChecker = $request->filled('checker_id') ? $request->checker_id : $deviation->checker_id;
        $logNotes   = $deviation->maker_deviation_notes ?? '';
        $logComment = $request->has('checker_comment') ? $request->checker_comment : $old_checker_comment;

        // Centralized Logging
        $this->logDeviationHistory(
            $deviation->id,
            'update',
            $old_status,
            $request->status,
            $logDetails,
            $logChecker,
            $logNotes,
            $logComment
        );

        return response()->json(['success' => true, 'status' => $request->status]);
    } 

    public function deleteDeviation($id){
        $deviation = ApplicationDeviation::findOrFail($id);

        $this->logDeviationHistory(
            $deviation->id,
            'delete',
            $deviation->status,            // Old Status
            $deviation->status,            // Current Status (Same on delete)
            $deviation->application_details,
            $deviation->checker_id,
            $deviation->maker_deviation_notes ?? '',
            $deviation->checker_comment ?? ''
        );

        $deviation->delete();

        return response()->json(['success' => true]);
    }

    private function logDeviationHistory($deviationId, $event, $oldStatus, $currentStatus, $details, $checkerId, $notes, $comment) {
        ApplicationDeviationLog::create([
            'application_deviation_id' => $deviationId,
            'application_details'      => $details,
            'action_by_id'             => Auth::id(),
            'checker_id'               => $checkerId,
            'remark'                   => json_encode([
                                            'notes'   => $notes,
                                            'comment' => $comment
                                          ]),
            'old_status'               => $oldStatus,
            'current_status'           => $currentStatus,
            'event'                    => $event,
            'created_at'               => now()
        ]);
    }

    public function getDeviationsByApplication(Request $request, $application_id){
        $category = $request->get('category', 'credit');
        $deviations = ApplicationDeviation::with(['maker', 'checker', 'deviationType'])
            ->where('consumer_application_id', $application_id)
            ->whereHas('deviationType', function($q) use ($category) {
                $q->where('category', $category);
            })
            ->orderBy('created_at', 'desc')
            ->get();

        return response()->json(['deviations' => $deviations]);
    }

    public function getAllUsers() {
        // $users = User::select('id', 'name')
        //     ->orderBy('name', 'asc')
        //     ->get();
        // return response()->json($users);
        return response()->json(true);
    }

    public function getTypes(Request $request){
        // Eloquent: Fetch all types that are not soft-deleted
        $category = $request->get('category', 'credit');
        $types = DeviationType::where('is_active', 1)
            ->where('category', $category)
            ->orderBy('created_at', 'asc')->get();

        return response()->json($types);
    }

        public function toggleTypeStatus(Request $request) {
        $request->validate([
            'id' => 'required|integer',
            'is_active' => 'required|boolean'
        ]);
        $type = DeviationType::findOrFail($request->id);
        $type->is_active = $request->is_active;
        $type->save();
        return response()->json(['success' => true]);
    }

    public function deleteType(Request $request) {
        $request->validate([
            'id' => 'required|integer'
        ]);

        $type = DeviationType::find($request->id);

        if($type) {
            // Soft delete logic: set is_active to 0
            $type->is_active = 0; 
            $type->save();
            
            return response()->json(['success' => true, 'message' => 'Type deleted successfully']);
        }
        return response()->json(['success' => false, 'message' => 'Type not found'], 404);
    }

    public function storeType(Request $request){
        $raw = $request->getContent();
        if (!$request->has('name') && !$request->has('id') && !empty($raw)) {
            $parsed = json_decode($raw, true);
            if (is_array($parsed) && isset($parsed['name'])) $request->merge($parsed);
            else { parse_str($raw, $urlParsed); if (is_array($urlParsed) && isset($urlParsed['name'])) $request->merge($urlParsed); }
        }

        try {
            if ($request->has('id')) {
                $request->validate(['id' => 'required|integer']);
            } else {
                $request->validate(['name' => 'required|string|max:191']);
            }
        } catch (\Illuminate\Validation\ValidationException $e) {
            return response()->json([
                'message' => 'The given data was invalid.',
                'errors' => $e->errors(),
                'debug_raw' => $raw,
                'debug_all' => $request->all(),
            ], 422);
        }

        $category = $request->get('category', 'credit');
        $approvers = $request->get('approver_user_ids', null);
        if (is_array($approvers)) $approvers = json_encode($approvers);

        if ($request->has('id')) {
            $type = DeviationType::findOrFail($request->id);
            if ($request->has('name')) $type->name = $request->name;
            $type->category = $category;
            
            // Merge existing approvers with new approvers
            $existingApprovers = [];
            if (!empty($type->approver_user_ids)) {
                $existingApprovers = is_string($type->approver_user_ids) ? json_decode($type->approver_user_ids, true) : $type->approver_user_ids;
                if (!is_array($existingApprovers)) $existingApprovers = [];
            }
            
            $newApprovers = $request->get('approver_user_ids', []);
            if (!is_array($newApprovers)) {
                $newApprovers = is_string($newApprovers) ? json_decode($newApprovers, true) : [];
            }
            
            $mergedApprovers = array_values(array_unique(array_merge($existingApprovers, $newApprovers)));
            $type->approver_user_ids = json_encode($mergedApprovers);
            
            $type->save();
        } else {
            $type = DeviationType::create([
                'name'       => $request->name,
                'category'   => $category,
                'approver_user_ids' => $approvers,
                'is_active'  => 1,
                'created_by' => Auth::id(),
            ]);
        }

        return response()->json([
            'success' => true, 
            'id'      => $type->id, 
            'name'    => $type->name 
        ]);
    }

    public function getLogs(Request $request){
        $applicationId = $request->application_id;

        $category = $request->get('category', 'credit');
        $deviationIds = ApplicationDeviation::withTrashed()
            ->where('consumer_application_id', $applicationId)
            ->whereHas('deviationType', function($q) use ($category) {
                $q->where('category', $category);
            })
            ->pluck('id');

        // eager load applicationDeviation -> deviationType and the actionBy user
        $logs = ApplicationDeviationLog::with([
                'applicationDeviation' => function ($q) {
                    $q->withTrashed()->with(['deviationType' => function ($q2) {
                        if (method_exists($q2->getModel(), 'trashed')) {
                            // If DeviationType also uses SoftDeletes, include trashed
                            $q2->withTrashed();
                        }
                    }]);
                },
                'actionBy'
            ])
            ->whereIn('application_deviation_id', $deviationIds)
            ->orderBy('created_at', 'desc')
            ->get();

        $result = $logs->map(function ($log) {
            return [
                'id' => $log->id,
                'application_deviation_id' => $log->application_deviation_id,
                'application_details' => $log->application_details,
                'deviation_type' => optional($log->applicationDeviation->deviationType)->name, // null-safe
                'action_by_name' => optional($log->actionBy)->name,
                'checker_name'  =>  optional($log->checker)->name,
                'remark' => $log->remark,
                'event' => $log->event,
                'old_status' => $log->old_status,
                'current_status' => $log->current_status,
                'created_at' => $log->created_at ? $log->created_at->format('Y-m-d H:i:s') : '',
            ];
        });

        return response()->json($result);
    }

    protected function sendNotification($checkerIds, int $consumerApplicationId, int $makerId, $categoryTitle = 'Credit'){
        $checkerIds = is_array($checkerIds) ? $checkerIds : [$checkerIds];
        $checkers = User::whereIn('id', $checkerIds)->whereNotNull('email')->get();
        
        if ($checkers->isEmpty()) {
            return; 
        }

        $emails = $checkers->pluck('email')->toArray();
        $maker = User::find($makerId);
        $application = ConsumerApplication::with('activeMerchant')
            ->find($consumerApplicationId);

        $categoryTitle = Str::ucfirst($categoryTitle);
        $losHost = rtrim(env('APP_URL'), '/');
        $hashLink = (strtolower($categoryTitle) === 'disbursement') ? '#nav-disbursement-deviation' : '#nav-application-deviation';
        $approvalLink = $losHost . '/admin/consumer-applications/edit/' . $consumerApplicationId . $hashLink;

        $mailData = [
            'name' => $checkers->count() === 1 ? $checkers->first()->name : 'Approver',
            'application_id' => $consumerApplicationId,
            'link' => $approvalLink,
            'customer_name' => $application->name ?? 'Unknown',
            'merchant_name' => optional($application->activeMerchant)->merchant_name ?? 'Unknown',
            'creator_name'  => $maker ? $maker->name : 'Unknown',
            'status' => Str::ucfirst($application->state),
            'category_title' => $categoryTitle,
            'logo_url'        => env('LENDER_KMGCP_LOGO')
        ];
        $fromEmail = _env('GLOBAL_EMAIL_REPLY_TO');
        Mail::send('admin.consumer-application.sections.deviation-mail', $mailData, function ($message) use ($emails, $fromEmail, $categoryTitle) {
            $message->to($emails)
                    ->from($fromEmail, 'Credit Fair')
                    ->replyTo($fromEmail)
                    ->subject("New {$categoryTitle} Deviation Created – Approval Required");
        });
    }

    protected function sendStatusUpdateNotification($deviation, $newStatus, $checkerName, $checkerComment, $deviationName) {
        $maker = User::find($deviation->maker_id);
        $application = ConsumerApplication::with('activeMerchant')->find($deviation->consumer_application_id);

        if (!$maker || empty($maker->email)) {
            return;
        }

        $categoryTitle = $deviation->deviationType ? $deviation->deviationType->category : 'Credit';
        $categoryTitle = Str::ucfirst($categoryTitle);

        $losHost = rtrim(env('APP_URL'), '/');
        $hashLink = (strtolower($categoryTitle) === 'disbursement') ? '#nav-disbursement-deviation' : '#nav-application-deviation';
        $link = $losHost . '/admin/consumer-applications/edit/' . $deviation->consumer_application_id . $hashLink;

        $mailData = [
            'name' => $maker->name,
            'application_id' => $deviation->consumer_application_id,
            'link' => $link,
            'customer_name' => $application->name ?? 'Unknown',
            'merchant_name' => optional($application->activeMerchant)->merchant_name ?? 'Unknown',
            'checker_name'  => $checkerName,
            'checker_comment' => $checkerComment,
            'deviation_name' => $deviationName,
            'category_title' => $categoryTitle,
            'action_status' => Str::ucfirst($newStatus),
            'status' => Str::ucfirst($application->state),
            'logo_url' => env('LENDER_KMGCP_LOGO')
        ];
        
        $fromEmail = _env('GLOBAL_EMAIL_REPLY_TO');
        Mail::send('admin.consumer-application.sections.deviation-status-mail', $mailData, function ($message) use ($maker, $fromEmail, $newStatus, $categoryTitle) {
            $message->to($maker->email)
                    ->from($fromEmail, 'Credit Fair')
                    ->replyTo($fromEmail)
                    ->subject("{$categoryTitle} Deviation Status Updated – {$newStatus}");
        });

        $this->sendDeviationSlackNotification('update', [
            'category' => $deviation->deviationType ? $deviation->deviationType->category : 'Credit',
            'newStatus' => $newStatus,
            'deviationName' => $deviationName,
            'customerName' => $application->name ?? 'Unknown',
            'appId' => $deviation->consumer_application_id,
            'checkerName' => $checkerName,
            'checkerComment' => $checkerComment
        ]);
    }

    protected function sendDeviationSlackNotification($actionType, $data) {
        $category = $data['category'] ?? 'Credit';

        $categoryTitle = Str::ucfirst($category);
        $losHost = rtrim(env('APP_URL'), '/');
        $hashLink = (strtolower($categoryTitle) === 'disbursement') ? '#nav-disbursement-deviation' : '#nav-application-deviation';
        $link = $losHost . '/admin/consumer-applications/edit/' . $data['appId'] . $hashLink;

        $slackTitle = "";
        
        if ($actionType === 'create') {
            $slackTitle = "<b>{$categoryTitle} Deviation</b><br><br>";
            $slackTitle .= "A new " . strtolower($categoryTitle) . " deviation is pending review.<br>";
            $slackTitle .= "<b>Type:</b> " . ($data['deviationName'] ?? 'Unknown') . "<br>";
            $slackTitle .= "<b>Customer:</b> " . ($data['customerName'] ?? 'Unknown') . "<br>";
            $slackTitle .= "<b>App ID:</b> {$data['appId']}<br>";
            if (!empty($data['notes'])) {
                $slackTitle .= "<b>Notes:</b> {$data['notes']}<br>";
            }
            $slackTitle .= "<b>Action Required:</b> Please review and take action.<br><br>";
            $slackTitle .= "<a href='{$link}'>Review Deviation</a>";
        } elseif ($actionType === 'update') {
            $slackTitle = "<b>{$categoryTitle} Deviation Status Updated</b><br><br>";
            $slackTitle .= "A {$categoryTitle} deviation status has been updated to <b>{$data['newStatus']}</b>.<br>";
            $slackTitle .= "<b>Type:</b> " . ($data['deviationName'] ?? 'Unknown') . "<br>";
            $slackTitle .= "<b>Customer:</b> " . ($data['customerName'] ?? 'Unknown') . "<br>";
            $slackTitle .= "<b>App ID:</b> {$data['appId']}<br>";
            $slackTitle .= "<b>Action By:</b> {$data['checkerName']}<br>";
            if (!empty($data['checkerComment'])) {
                $slackTitle .= "<b>Comment:</b> {$data['checkerComment']}<br>";
            }
            $slackTitle .= "<br><a href='{$link}'>Review Deviation</a>";
        }

        if (strtolower($category) !== 'disbursement') {
            return;
        }

        try {
            $slackGroup = Slack::$DISBURSEMENT_QUERY;
            (new Slack(new Request([
                'title' => $slackTitle,
                'fields' => [],
                'hide_footer' => true,
                'group' => $slackGroup
            ])))->send();
        } catch (\Exception $e) {}
    }
}