<?php

namespace App\Http\Controllers;

use App\Models\MutationOutRequest;
use App\Models\MutationOutRequestDetail;
use App\Models\Department;
use App\Models\ItemDetail;
use App\Models\Company;
use App\Models\Warehouse;
use App\Models\ItemUnit;
use App\Models\Item;
use App\Models\Process;
use App\Models\ProcessingPlan;
use App\Models\ProductionPlan;
use App\Models\ProductionPlanDetail;
use App\Models\Periode;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\QueryException;
use App\Models\DeleteLog;
use App\Http\Controllers\Module;
use Carbon\Carbon;

class MutationOutRequestController extends Controller
{
    public function index($dateStart,$dateEnd)
    {
        try {
            $mutationOutRequest = MutationOutRequest::with('details.item','details.unit','warehouse', 'department')
            ->whereBetween('document_date',[$dateStart,$dateEnd])
            ->whereIn('department_id',$this->getDepartment())->get();
            $periods = Periode::all();
            foreach($mutationOutRequest as $mor){
                $mor->closed =  Module::checkPeriodeBack($periods,$mor->document_date);
            }
            return response()->json(["mutationOutRequest" => $mutationOutRequest, "role" => $this->getRole()]);
        } catch (\Exception $e) {
            Log::error('Failed to fetch mutation out requests', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return response()->json(['error' => 'Failed to fetch mutation out request'], 500);
        }
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'document_date' => 'required|date',
            'plan_id' => 'nullable|integer|exists:production_plans,id',
            'processing_plan_id' => 'nullable|integer|exists:processing_plans,id',
            'warehouse_id' => 'required|integer|exists:warehouse,id',
            'notes' => 'nullable|string',
            'department_id' => 'required|integer|exists:department,id',
            'details' => 'required|array|min:1',
            'details.*.item_id' => 'required|integer|exists:item_details,item_id',
            'details.*.qty' => 'required|numeric|min:0',
            'details.*.unit_id' => 'required|integer|exists:item_units,id',
            'details.*.notes' => 'nullable|string',
            'details.*.item_date' => 'nullable|date',
        ]);

        DB::beginTransaction();
        try {
            $company = Company::first();
            $department = Department::find($request->department_id);
            $date = Carbon::parse($request->document_date);
            $nobukti = Module::generateDocumentNumber($company->companyCode, $department->department_code, $date->month, $date->year, "MOR", 1);

            $userId = Auth::user()->id;
            $mutationOutRequest = MutationOutRequest::create([
                'mutation_out_request_number' => $nobukti,
                'document_date' => $request->document_date,
                'plan_id' => $request->plan_id,
                'processing_plan_id' => $request->processing_plan_id,
                'warehouse_id' => $request->warehouse_id,
                'notes' => $request->notes ?? '',
                'department_id' => $request->department_id,
                'status' => 'pending',
                'created_by' => $userId,
                'updated_by' => $userId
            ]);

            foreach ($request->details as $detail) {
                $itemD = ItemDetail::with(["item"])->where("item_id", $detail['item_id'])->where("unit_id", $detail['unit_id'])->first();
                $mutationOutRequestDetail = MutationOutRequestDetail::create([
                    'mutation_out_request_id' => $mutationOutRequest->id,
                    'item_date' => $detail['item_date'],
                    'item_id' => $detail['item_id'],
                    'qty' => $detail['qty'],
                    'unit_id' => $detail['unit_id'],
                    'base_qty' => $itemD->conversion,
                    'notes' => $detail['notes'] ?? '',
                    'department_id' => $mutationOutRequest->department_id,
                    'qty_left' => $detail['qty'],
                    'created_by' => $userId,
                    'updated_by' => $userId,
                ]);
            }

            DB::commit();
            return response()->json(null, 201);
        } catch (\Exception $e) {
            DB::rollback();
            Log::error('Failed to create mutation out request', [
                'error' => $e->getMessage(),
                'request_data' => $request->all(),
                'trace' => $e->getTraceAsString()
            ]);
            return response()->json(['error' => $e->getMessage()], 422);
        }
    }

    public function show($id)
    {
        try {
            $mutationOutRequest = MutationOutRequest::with(['details.item', 'details.unit', 'department'])->findOrFail($id);
            return response()->json($mutationOutRequest);
        } catch (\Exception $e) {
            Log::error('Failed to fetch mutation out request', [
                'id' => $id,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return response()->json(['error' => 'Mutation out request not found or failed to fetch'], 404);
        }
    }

    public function update(Request $request, $id)
    {
        DB::beginTransaction();
        try {
            $mutationOutRequest = MutationOutRequest::findOrFail($id);
            $validated = $request->validate([
                'document_date' => 'required|date',
                'plan_id' => 'nullable|integer|exists:production_plans,id',
                'processing_plan_id' => 'nullable|integer|exists:processing_plans,id',
                'warehouse_id' => 'required|integer|exists:warehouse,id',
                'notes' => 'nullable|string',
                'department_id' => 'required|integer|exists:department,id',
                'details' => 'required|array|min:1',
                'details.*.item_id' => 'required|integer|exists:item_details,item_id',
                'details.*.qty' => 'required|numeric|min:0',
                'details.*.unit_id' => 'required|integer|exists:item_units,id',
                'details.*.notes' => 'nullable|string',
                'details.*.item_date' => 'nullable|date',
            ]);
            $company = Company::first();
            $date = Carbon::parse($request->document_date);

            $userId = Auth::user()->id;

            $mutationOutRequest->update([
                'document_date' => $request->document_date,
                'plan_id' => $request->plan_id,
                'processing_plan_id' => $request->processing_plan_id,
                'warehouse_id' => $request->warehouse_id,
                'notes' => $request->notes ?? '',
                'department_id' => $request->department_id,
                'updated_by' => $userId,
            ]);

            $mutationOutRequest->details()->delete();

            foreach ($request->details as $detail) {
                $itemD = ItemDetail::with(["item"])->where("item_id", $detail['item_id'])->where("unit_id", $detail['unit_id'])->first();
                $mutationOutRequestDetail = MutationOutRequestDetail::create([
                    'mutation_out_request_id' => $mutationOutRequest->id,
                    'item_date' => $detail['item_date'],
                    'item_id' => $detail['item_id'],
                    'qty' => $detail['qty'],
                    'unit_id' => $detail['unit_id'],
                    'base_qty' => $itemD->conversion,
                    'notes' => $detail['notes'] ?? '',
                    'department_id' => $mutationOutRequest->department_id,
                    'qty_left' => $detail['qty'],
                    'created_by' => $mutationOutRequest->created_by,
                    'updated_by' => $userId,
                ]);
            }

            DB::commit();
            return response()->json(null, 200);
        } catch (\Exception $e) {
            DB::rollback();
            Log::error('Failed to update mutation out request', [
                'id' => $id,
                'error' => $e->getMessage(),
                'request_data' => $request->all(),
                'trace' => $e->getTraceAsString()
            ]);
            return response()->json(['error' => $e->getMessage()], 422);
        }
    }

    public function destroy(Request $request, $id)
    {
        try {
            DB::beginTransaction();

            $mutationOutRequest = MutationOutRequest::findOrFail($id);
            $mutationOutRequest->details()->delete();
            $mutationOutRequest->delete();

            DeleteLog::create([
                'document_number' => $mutationOutRequest->mutation_out_request_number,
                'document_date' => $mutationOutRequest->document_date,
                'delete_notes' => $request->delete_notes ?? 'No notes provided',
                'company_code' => null,
                'department_code' => $mutationOutRequest->department_id,
                'deleted_by' => Auth::check() ? Auth::user()->name : 'system',
                'type' => 'Mutation Out Request',
            ]);

            DB::commit();
            return response()->json(null, 204);
        } catch (QueryException $e) {
            DB::rollBack();
            Log::error('Failed to delete mutation out request due to database error', [
                'id' => $id,
                'error' => $e->getMessage(),
                'sql_error_code' => $e->getCode(),
                'trace' => $e->getTraceAsString()
            ]);
            if ($e->getCode() === '23000') {
                return response()->json(['error' => 'Cannot delete mutation out request because it is being used in other records'], 422);
            }
            return response()->json(['error' => 'Failed to delete mutation out request due to database error'], 500);
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Failed to delete mutation out request due to an unexpected error', [
                'id' => $id,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return response()->json(['error' => 'Failed to delete mutation out request due to an unexpected error'], 500);
        }
    }

    public function cancel(Request $request,$id){
        DB::beginTransaction();
        try {
            $mutationOutRequest = MutationOutRequest::find($id);
            if (!$mutationOutRequest) {
                return response()->json(['message' => 'Mutation Out Request not found'], 404);
            }

            $validator = $request->validate([
                'cancel_notes' => 'required|string',
            ]);

            $mutationOutRequest->status = "canceled";
            $mutationOutRequest->cancel_notes = $request->cancel_notes;
            $mutationOutRequest->canceled_at = now();
            $mutationOutRequest->canceled_by = auth()->id();
            $mutationOutRequest->save();

            foreach ($mutationOutRequest->details as $detail) {
                if($detail->qty_left > 0){
                    $detail->update(["qty_left" => 0,"is_cancel" => 1]);
                }
            }

            DB::commit();
            return response()->json($mutationOutRequest->load('details'));
        } catch (\Exception $e) {
            DB::rollback();
            \Log::error('Update Error: ' . $e->getMessage());
            return response()->json(['error' => 'Server error'], 500);
        }
    }

    public function getMasterData()
    {
        try {
            $Listdepartment = $this->getDepartment();
            //$departments = Department::whereIn("id",$Listdepartment)->get();
            //khusus mutasi tdk dibatasi saat input
            $departments = Department::get();
            $gudangUser = DB::table("user_warehouse")->where("user_id",Auth::user()->id)->select("warehouse_id")->get()->pluck("warehouse_id")->toArray();
            $warehouses = Warehouse::whereIn("id",$gudangUser)->get();
            $items = Item::with(['itemDetails' => function($q) {
                $q->where("is_active",1);
            },'itemDetails.unit'])->get();

            $magic = DB::select("select x.*,y_in.item_name item_name_in from (
            select process.id,arr_in.id::json->'id' id_in
            from process,jsonb_array_elements(input_item_id) with ordinality arr_in(id)
            ) x join items y_in on x.id_in::varchar = y_in.id::varchar");
            $pc = collect($magic);

            //Production
            $productionPlans = [];$itemProcessProduction = [];$itemProcessProcessing = [];
            if(in_array(5, $Listdepartment)){
                $productionPlans = ProductionPlan::with(['item','details'])->where('status','<>','Completed')->get();
                foreach($productionPlans as $pp){
                    foreach($pp->details()->get() as $dePP){
                        $tpc = $pc->where("id",$dePP->process_id);
                        foreach($tpc as $i){
                            $itemProcessProduction[] = $i->item_name_in;
                        }
                    }
                    $pp->itemProcessProduction = implode(" , ",$itemProcessProduction);
                }
            }

            //Processing
            $processingplans = [];
            if(in_array(4, $Listdepartment)){
                $processingplans = ProcessingPlan::with(['item'])->where('status','<>','Completed')->get();
                foreach($processingplans as $pps){
                    foreach($pps->details()->get() as $dePPc){
                        $tpcs = $pc->where("id",$dePPc->process_id);
                        foreach($tpcs as $i){
                            $itemProcessProcessing[] = $i->item_name_in;
                        }
                    }
                    $pps->itemProcessProcessing = implode(" , ",$itemProcessProcessing);
                }
            }

            $itemDetails = ItemDetail::with(['item'])->get();
            $itemUnit = ItemUnit::get();
            $process = Process::all();

            $company = Company::first();
            $date = Carbon::now();
            $nobukti = Module::generateDocumentNumber($company->companyCode, $departments->first()->department_code, $date->month, $date->year, "MOR", 0);

            $response = [
                'departments' => $departments,
                'warehouses' => $warehouses,
                'production_plans' => $productionPlans,
                'processing_plans' => $processingplans,
                'items' => $items,
                'item_unit' => $itemUnit,
                'process' => $process,
                'item_details' => $itemDetails,
                'nobukti' => $nobukti,
                'role' => $this->getRole()
            ];
            return response()->json($response);
        } catch (\Exception $e) {
            dd($e->getMessage());
            Log::error('Failed to fetch master data', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return response()->json(['error' => 'Failed to fetch master data'], 500);
        }
    }

    public function getNomorBukti(Request $request)
    {
        try {
            $company = Company::first();
            $department = Department::find($request->department_id);
            $date = Carbon::parse($request->document_date);
            $nobukti = Module::generateDocumentNumber($company->companyCode, $department->department_code, $date->month, $date->year, "MOR", 0);
            return response()->json($nobukti);
        } catch (\Exception $e) {
            Log::error('Failed to generate document number', [
                'error' => $e->getMessage(),
                'request_data' => $request->all(),
                'trace' => $e->getTraceAsString()
            ]);
            return response()->json(['error' => 'Failed to generate document number'], 500);
        }
    }

    public function mutationTracking($dateStart,$dateEnd){
        $first = DB::table("mutation_outs as x")
        ->join("mutation_out_details as y","x.id","=","y.mutation_out_id")
        ->join("items as i","i.id","=","y.item_id")
        ->join("item_units as iu","iu.id","=","y.unit")
        ->leftJoin("mutation_in_details as z","z.mutation_out_detail_id","=","y.id")
        ->leftJoin("mutation_ins as zheader","zheader.id","=","z.mutation_in_id")
        ->leftJoin("warehouse as wh","wh.id","=","x.warehouse_origin_id")
        ->leftJoin("warehouse as whd","whd.id","=","zheader.warehouse_destination_id")
        ->whereBetween("x.document_date",[$dateStart,$dateEnd])
        ->whereNull("y.mutation_out_request_id")
        ->select("i.item_name", "iu.unit_name", DB::raw("null as qty_request"),  "wh.warehouse_name as warehouse_request", 
        DB::raw("null as mutation_out_request_number"), DB::raw("null as mutation_out_request_document_date"), "x.mutation_out_number", 
        "x.document_date as mutation_out_document_date", "y.qty as qty_out", "zheader.mutation_in_number", "zheader.document_date as mutation_in_document_date", 
        "z.qty as qty_in", "whd.warehouse_name as warehouse_tujuan");

        $hasil = DB::table("mutation_out_request as x")
        ->join("mutation_out_request_details as y","x.id","=","y.mutation_out_request_id")
        ->join("items as i","i.id","=","y.item_id")
        ->join("item_units as iu","iu.id","=","y.unit_id")
        ->leftJoin("mutation_out_details as w","w.mutation_out_request_detail_id","=","y.id")
        ->leftJoin("mutation_outs as wheader","wheader.id","=","w.mutation_out_id")
        ->leftJoin("mutation_in_details as z","z.mutation_out_detail_id","=","w.id")
        ->leftJoin("mutation_ins as zheader","zheader.id","=","z.mutation_in_id")
        ->leftJoin("warehouse as wh","wh.id","=","x.warehouse_id")
        ->leftJoin("warehouse as whd","whd.id","=","zheader.warehouse_destination_id")
        ->whereBetween("x.document_date",[$dateStart,$dateEnd])
        ->select("i.item_name","iu.unit_name","y.qty as qty_request","wh.warehouse_name as warehouse_request","x.mutation_out_request_number",
        "x.document_date as mutation_out_request_document_date","wheader.mutation_out_number","wheader.document_date as mutation_out_document_date","w.qty as qty_out",
        "zheader.mutation_in_number","zheader.document_date as mutation_in_document_date","z.qty as qty_in","whd.warehouse_name as warehouse_tujuan")
        ->union($first)
        ->get();

        
        return response()->json(["report" => $hasil, "role" => $this->getRole()], 200);
    }
}
