<?php

namespace App\Services;

use App\Models\Batches;
use App\Models\Culling;
use App\Models\Transaction;
use App\Models\User;
use App\Services\AgeCheckerService;
use App\Services\AuditTrailService;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class CullingService
{
    public function __construct(
        protected AuditTrailService $auditTrail,
        protected AgeCheckerService $ageChecker
    ) {}

    public function getAll()
    {
        $user = Auth()->user();

        $culling = Culling::where("culled_by_id", $user->id)->dynamicPaginate();

        return $culling;
    }

    public function getById(int $id)
    {
        $user = Auth()->user();

        $culling = Culling::with("batch")->where("culled_by_id", $user->id)->find($id);

        return $culling;
    }

    public function create(array $data, $attachments = null, $signature = null)
    {
        $batch = Batches::with("loading")->find($data["batch_id"]);
        $age_checker = $this->ageChecker->check($batch->start_date);
        $user = Auth()->user();

        $user_eid_name = $user->id_prefix . "-" . $user->id_no . "-" .  $user->first_name . " " . $user->last_name;

        $caretaker = User::where("id", $data["caretaker_id"])->first();
        $caretaker_eid_name = $caretaker->id_prefix . "-" . $caretaker->id_no . "-" .  $caretaker->first_name . " " . $caretaker->last_name;

        $attachmentFilenames = [];
        $attachmentPaths = [];

        $signatureName = null;
        $signaturePath = null;

        if ($attachments && is_array($attachments)) {
            foreach ($attachments as $attachment) {
                $randomName = Str::random(40) . '.' . $attachment->getClientOriginalExtension();

                $path = $attachment->storeAs('culling/attachments', $randomName, 'public');

                $attachmentFilenames[] = $attachment->getClientOriginalName();
                $attachmentPaths[] = $path;
            }
        }

        $culling = Culling::create([
            "batch_id" => $batch->id,
            "culling_date" => $data["culling_date"],
            "age_in_days" => $age_checker["age"],
            "population_before_cull" => $data["net_population"],
            "classification" => $age_checker["classification"],
            "undersized" => $data["undersized"],
            "weak" => $data["weak"],
            "chick_infection" => $data["chick_infection"],
            "black_button" => $data["black_button"],
            "red_hocks" => $data["red_hocks"],
            "dehydrated" => $data["dehydrated"],
            "diarrhea" => $data["diarrhea"],
            "lameness" => $data["lameness"],
            "split" => $data["split"],
            "pvr" => $data["pvr"],
            "crd" => $data["crd"],
            "rhinitis" => $data["rhinitis"],
            "tremors" => $data["tremors"],
            "nd" => $data["nd"],
            "others_text" => $data["others_text"],
            "others" => $data["others"],
            "synced_status" => $data["synced_status"] ?? "unsynced",
            "synced_at" => $data["synced_at"] ?? NULL,
            "synced_id" => $data["synced_id"] ?? NULL,
            "remarks" => $data["remarks"],
            "culled_by_id" => $user->id,
            "culled_by_name" => $user_eid_name,
            "caretaker_id" => $caretaker->id,
            "caretaker_name" => $caretaker_eid_name,
            "attachment_filename" => !empty($attachmentFilenames) ? $attachmentFilenames : null,
            "attachment_path" => !empty($attachmentPaths) ? $attachmentPaths : null,
        ]);

        if ($signature) {
            $signatureFilename = 'signature-' . $culling->id . '-' . $caretaker->id . '.' . $signature->getClientOriginalExtension();

            $signaturePath = $signature->storeAs('culling/signatures', $signatureFilename, 'public');

            $culling->update([
                'signature_name' => $signature->getClientOriginalName(),
                'signature_path' => $signaturePath
            ]);
        }

        $batch->update([
            "current_population" => $data["net_population"] - $data["total_cull"],
            "total_culled" => $batch->total_culled + $data["total_cull"]
        ]);

        Transaction::create([
            "batch_id" => $batch->id,
            "farm_id" => $batch->farm_id,
            "farm_name" => $batch->farm_name,
            "building_id" => $batch->building_id,
            "building_name" => $batch->building_name,
            "transaction_id" => $culling->id,
            "transaction_type" => "culling",
            "quantity" => -$culling->total_culled,
            "transaction_date" => $culling->culling_date,
            "remarks" => "Culling - Total: {$culling->total_culled}",
            "created_by" => auth()->id(),
        ]);

        $this->auditTrail->log(
            __("models.transaction"),
            __("models.culling"),
            $culling->id,
            __("models.lc_created"),
            $culling,
            null,
            $data
        );

        return $culling;
    }

    public function update(array $data, $id, $attachments = null, $signature = null)
    {
        $culling = Culling::find($id);

        if (!$culling) {
            return null;
        }

        $batch = Batches::with("loading")->find($data["batch_id"]);
        $age_checker = $this->ageChecker->check($batch->start_date);
        $user = Auth()->user();

        $user_eid_name = $user->id_prefix . "-" . $user->id_no . "-" . $user->first_name . " " . $user->last_name;

        $caretaker = User::where("id", $data["caretaker_id"])->first();
        $caretaker_eid_name = $caretaker->id_prefix . "-" . $caretaker->id_no . "-" . $caretaker->first_name . " " . $caretaker->last_name;

        $attachmentFilenames = $culling->attachment_filename ?? [];
        $attachmentPaths = $culling->attachment_path ?? [];

        if ($attachments && is_array($attachments)) {
            foreach ($attachments as $attachment) {
                $randomName = Str::random(40) . '.' . $attachment->getClientOriginalExtension();
                $path = $attachment->storeAs('culling/attachments', $randomName, 'public');

                $attachmentFilenames[] = $attachment->getClientOriginalName();
                $attachmentPaths[] = $path;
            }
        }

        $oldBatch = Batches::find($culling->batch_id);
        if ($oldBatch) {
            $oldBatch->update([
                "current_population" => $oldBatch->current_population + $culling->total_culled,
                "total_culled"       => $oldBatch->total_culled - $culling->total_culled,
            ]);
        }

        $culling->update([
            "batch_id"               => $batch->id,
            "culling_date"           => $data["culling_date"],
            "age_in_days"            => $age_checker["age"],
            "population_before_cull" => $data["net_population"],
            "classification"         => $age_checker["classification"],
            "undersized"             => $data["undersized"],
            "weak"                   => $data["weak"],
            "chick_infection"        => $data["chick_infection"],
            "black_button"           => $data["black_button"],
            "red_hocks"              => $data["red_hocks"],
            "dehydrated"             => $data["dehydrated"],
            "diarrhea"               => $data["diarrhea"],
            "lameness"               => $data["lameness"],
            "split"                  => $data["split"],
            "pvr"                    => $data["pvr"],
            "crd"                    => $data["crd"],
            "rhinitis"               => $data["rhinitis"],
            "tremors"                => $data["tremors"],
            "nd"                     => $data["nd"],
            "others_text"            => $data["others_text"],
            "others"                 => $data["others"],
            "synced_status"          => $data["synced_status"] ?? "unsynced",
            "synced_at"              => $data["synced_at"] ?? null,
            "synced_id"              => $data["synced_id"] ?? null,
            "remarks"                => $data["remarks"],
            "culled_by_id"           => $user->id,
            "culled_by_name"         => $user_eid_name,
            "caretaker_id"           => $caretaker->id,
            "caretaker_name"         => $caretaker_eid_name,
            "attachment_filename"    => !empty($attachmentFilenames) ? $attachmentFilenames : null,
            "attachment_path"        => !empty($attachmentPaths) ? $attachmentPaths : null,
        ]);

        if ($signature) {
            if ($culling->signature_path) {
                Storage::disk('public')->delete($culling->signature_path);
            }

            $signatureFilename = 'signature-' . $culling->id . '-' . $caretaker->id . '.' . $signature->getClientOriginalExtension();
            $signaturePath = $signature->storeAs('culling/signatures', $signatureFilename, 'public');

            $culling->update([
                'signature_name' => $signature->getClientOriginalName(),
                'signature_path' => $signaturePath,
            ]);
        }

        $batch->update([
            "current_population" => $data["net_population"] - $data["total_cull"],
            "total_culled"       => $batch->total_culled + $data["total_cull"],
        ]);

        Transaction::where("batch_id", $batch->id)
            ->where("transaction_type", "culling")
            ->where("transaction_date", $culling->culling_date)
            ->update([
                "quantity"         => -$culling->total_culled,
                "transaction_date" => $culling->culling_date,
                "remarks"          => "Culling - Total: {$culling->total_culled}",
            ]);

        $this->auditTrail->log(
            __("models.transaction"),
            __("models.culling"),
            $culling->id,
            __("models.lc_updated"),
            $culling,
            null,
            $data
        );

        return $culling->fresh();
    }

    public function toggleArchived(int $id) {}

    public function viewAttachment($id)
    {
        $culling = Culling::find($id);

        if (!$culling) {
            return null;
        }

        if (!$culling->attachment_path) {
            return [];
        }

        $attachments = collect($culling->attachment_path)->map(function ($path, $index) use ($culling) {

            if (Storage::disk('public')->exists($path)) {
                return [
                    'file_name' => $culling->attachment_filename[$index] ?? null,
                    'url' => Storage::disk('public')->url($path),
                ];
            }
        })->filter()->values();

        return $attachments;
    }

    public function viewSignature($id)
    {
        $culling = Culling::find($id);

        if (!$culling) {
            return null;
        }

        if (
            !$culling->signature_path ||
            !Storage::disk("public")->exists($culling->signature_path)
        ) {
            abort(404, "Attachment not found");
        }

        return Storage::disk("public")->response($culling->signature_path);
    }

    private function syncCullingRecord(array $cullingData, string $dataHash, array $attachments, $signature, ?Culling $existing): Culling
    {
        $batch              = Batches::with('loading')->find($cullingData['batch_id']);
        $age_checker        = $this->ageChecker->check($batch->start_date);
        $user               = Auth()->user();
        $user_eid_name      = $user->id_prefix . "-" . $user->id_no . "-" . $user->first_name . " " . $user->last_name;
        $caretaker          = User::find($cullingData['caretaker_id']);
        $caretaker_eid_name = $caretaker->id_prefix . "-" . $caretaker->id_no . "-" . $caretaker->first_name . " " . $caretaker->last_name;

        // Handle attachments
        $attachmentFilenames = $existing->attachment_filename ?? [];
        $attachmentPaths     = $existing->attachment_path ?? [];

        if (!empty($attachments) && is_array($attachments)) {
            foreach ($attachments as $attachment) {
                $randomName = Str::random(40) . '.' . $attachment->getClientOriginalExtension();
                $path       = $attachment->storeAs('culling/attachments', $randomName, 'public');

                $attachmentFilenames[] = $attachment->getClientOriginalName();
                $attachmentPaths[]     = $path;
            }
        }

        $culling = Culling::updateOrCreate(
            ['synced_id' => $cullingData['sync_id']],
            [
                "batch_id"               => $batch->id,
                "culling_date"           => $cullingData['culling_date'],
                "age_in_days"            => $age_checker['age'],
                "population_before_cull" => $cullingData['net_population'],
                "classification"         => $age_checker['classification'],
                "undersized"             => $cullingData['undersized'],
                "weak"                   => $cullingData['weak'],
                "chick_infection"        => $cullingData['chick_infection'],
                "black_button"           => $cullingData['black_button'],
                "red_hocks"              => $cullingData['red_hocks'],
                "dehydrated"             => $cullingData['dehydrated'],
                "diarrhea"               => $cullingData['diarrhea'],
                "lameness"               => $cullingData['lameness'],
                "split"                  => $cullingData['split'],
                "pvr"                    => $cullingData['pvr'],
                "crd"                    => $cullingData['crd'],
                "rhinitis"               => $cullingData['rhinitis'],
                "tremors"                => $cullingData['tremors'],
                "nd"                     => $cullingData['nd'],
                "others_text"            => $cullingData['others_text'],
                "others"                 => $cullingData['others'],
                "synced_status"          => "synced",
                "synced_at"              => Carbon::now(),
                "synced_id"              => $cullingData['sync_id'],
                "remarks"                => $cullingData['remarks'] ?? null,
                "culled_by_id"           => $user->id,
                "culled_by_name"         => $user_eid_name,
                "caretaker_id"           => $caretaker->id,
                "caretaker_name"         => $caretaker_eid_name,
                "attachment_filename"    => !empty($attachmentFilenames) ? $attachmentFilenames : null,
                "attachment_path"        => !empty($attachmentPaths) ? $attachmentPaths : null,
                "data_hash"              => $dataHash,
            ]
        );

        // Handle signature
        if ($signature) {
            if (!$culling->wasRecentlyCreated && $culling->signature_path) {
                Storage::disk('public')->delete($culling->signature_path);
            }

            $signatureFilename = 'signature-' . $culling->id . '-' . $caretaker->id . '.' . $signature->getClientOriginalExtension();
            $signaturePath     = $signature->storeAs('culling/signatures', $signatureFilename, 'public');

            $culling->update([
                'signature_name' => $signature->getClientOriginalName(),
                'signature_path' => $signaturePath,
            ]);
        }

        // Update batch population
        $oldTotalCull = $culling->wasRecentlyCreated ? 0 : ($existing->total_culled ?? 0);

        $batch->update([
            "current_population" => $cullingData['net_population'] - $cullingData['total_cull'],
            "total_culled"       => $batch->total_culled + $cullingData['total_cull'] - $oldTotalCull,
        ]);

        // Upsert transaction
        Transaction::updateOrCreate(
            [
                "batch_id"         => $batch->id,
                "transaction_type" => "culling",
                "transaction_id"   => $culling->id,
            ],
            [
                "farm_id"          => $batch->farm_id,
                "farm_name"        => $batch->farm_name,
                "building_id"      => $batch->building_id,
                "building_name"    => $batch->building_name,
                "quantity"         => -$cullingData['total_cull'],
                "transaction_date" => $cullingData['culling_date'],
                "remarks"          => "Culling - Total: {$cullingData['total_cull']}",
                "created_by"       => $user->id,
            ]
        );

        $this->auditTrail->log(
            __('models.transaction'),
            __('models.culling'),
            $culling->id,
            $culling->wasRecentlyCreated ? __('models.lc_created') : __('models.lc_updated'),
            $culling,
            null,
            $cullingData
        );

        return $culling;
    }

    public function syncTransaction(array $data)
    {
        $cullings = $data['cullings'] ?? [];
        $files    = $data['attachments'] ?? [];

        $synced  = [];
        $failed  = [];
        $skipped = [];

        foreach ($cullings as $index => $cullingData) {
            try {
                $hashFields = collect($cullingData)->except(['sync_id'])->toArray();
                $dataHash   = md5(json_encode($hashFields));

                $existing = Culling::where('synced_id', $cullingData['sync_id'])->first();

                if ($existing && $existing->data_hash === $dataHash) {
                    $skipped[] = [
                        'sync_id'    => $cullingData['sync_id'],
                        'culling_id' => $existing->id,
                        'reason'     => 'No changes detected',
                    ];
                    continue;
                }

                $attachments = $files[$index]['attachments'] ?? [];
                $signature   = $files[$index]['signature'] ?? null;

                $culling = $this->syncCullingRecord($cullingData, $dataHash, $attachments, $signature, $existing);

                $synced[] = [
                    'sync_id'    => $cullingData['sync_id'],
                    'culling_id' => $culling->id,
                    'action'     => $culling->wasRecentlyCreated ? 'created' : 'updated',
                ];
            } catch (\Exception $e) {
                $failed[] = [
                    'sync_id' => $cullingData['sync_id'] ?? null,
                    'index'   => $index,
                    'error'   => $e->getMessage(),
                ];
            }
        }

        return [
            'synced'  => $synced,
            'skipped' => $skipped,
            'failed'  => $failed,
        ];
    }
}
