<?php

namespace App\Services\Design;

use App\Models\Design\BomTemplate;
use App\Models\Design\BomTemplateItem;
use App\Models\Design\ModelVersion;
use App\Models\Materials\Material;
use App\Models\Products\Product;
use App\Services\Service;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class BomTemplateService extends Service
{
    /** 페이징 목록(옵션: 모델버전 필터) */
    public function paginate(?int $modelVersionId, int $page = 1, int $size = 20): LengthAwarePaginator
    {
        $tenantId = $this->tenantId();

        $q = BomTemplate::query()->where('tenant_id', $tenantId);

        if ($modelVersionId) {
            $q->where('model_version_id', $modelVersionId);
        }

        return $q->orderByDesc('is_primary')
            ->orderBy('name')
            ->paginate($size, ['*'], 'page', $page);
    }

    /** 템플릿 upsert(name 기준) */
    public function upsertTemplate(int $modelVersionId, string $name = 'Main', bool $isPrimary = true, ?string $notes = null): BomTemplate
    {
        $tenantId = $this->tenantId();

        $mv = ModelVersion::query()
            ->where('tenant_id', $tenantId)
            ->find($modelVersionId);

        if (! $mv) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        return DB::transaction(function () use ($tenantId, $mv, $name, $isPrimary, $notes) {
            $tpl = BomTemplate::query()
                ->where('model_version_id', $mv->id)
                ->where('name', $name)
                ->first();

            $action = 'created';
            $before = null;

            if (! $tpl) {
                $tpl = BomTemplate::create([
                    'tenant_id' => $tenantId,
                    'model_version_id' => $mv->id,
                    'name' => $name,
                    'is_primary' => $isPrimary,
                    'notes' => $notes,
                ]);
            } else {
                $action = 'updated';
                $before = $tpl->toArray();
                $tpl->fill(['is_primary' => $isPrimary, 'notes' => $notes])->save();
            }

            if ($isPrimary) {
                BomTemplate::query()
                    ->where('model_version_id', $mv->id)
                    ->where('id', '<>', $tpl->id)
                    ->update(['is_primary' => false]);
            }

            // 감사 로그
            app(\App\Services\Audit\AuditLogger::class)->log(
                tenantId: $tenantId,
                targetType: 'bom_template',
                targetId: $tpl->id,
                action: $action,
                before: $before,
                after: $tpl->toArray()
            );

            return $tpl;
        });
    }

    /** 템플릿 메타 수정 */
    public function updateTemplate(int $templateId, array $data): BomTemplate
    {
        $tenantId = $this->tenantId();

        $tpl = BomTemplate::query()
            ->where('tenant_id', $tenantId)
            ->find($templateId);

        if (! $tpl) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        $name = $data['name'] ?? $tpl->name;
        if ($name !== $tpl->name) {
            $dup = BomTemplate::query()
                ->where('model_version_id', $tpl->model_version_id)
                ->where('name', $name)
                ->where('id', '<>', $tpl->id)
                ->exists();
            if ($dup) {
                throw ValidationException::withMessages(['name' => __('error.duplicate')]);
            }
        }

        $before = $tpl->toArray();
        $tpl->fill($data)->save();

        if (array_key_exists('is_primary', $data) && $data['is_primary']) {
            // 다른 템플릿 대표 해제
            BomTemplate::query()
                ->where('model_version_id', $tpl->model_version_id)
                ->where('id', '<>', $tpl->id)
                ->update(['is_primary' => false]);
        }

        // 감사 로그
        app(\App\Services\Audit\AuditLogger::class)->log(
            tenantId: $tenantId,
            targetType: 'bom_template',
            targetId: $tpl->id,
            action: 'updated',
            before: $before,
            after: $tpl->toArray()
        );

        return $tpl;
    }

    /** 템플릿 삭제(soft) */
    public function deleteTemplate(int $templateId): void
    {
        $tenantId = $this->tenantId();

        $tpl = BomTemplate::query()
            ->where('tenant_id', $tenantId)
            ->find($templateId);

        if (! $tpl) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        $before = $tpl->toArray();
        $tpl->delete();

        // 감사 로그
        app(\App\Services\Audit\AuditLogger::class)->log(
            tenantId: $tenantId,
            targetType: 'bom_template',
            targetId: $tpl->id,
            action: 'deleted',
            before: $before,
            after: null
        );
    }

    /** 템플릿 상세 (옵션: 항목 포함) */
    public function show(int $templateId, bool $withItems = false): BomTemplate
    {
        $tenantId = $this->tenantId();

        $q = BomTemplate::query()->where('tenant_id', $tenantId);
        if ($withItems) {
            $q->with(['items' => fn ($w) => $w->orderBy('sort_order')]);
        }

        $tpl = $q->find($templateId);
        if (! $tpl) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        return $tpl;
    }

    /** 항목 일괄 치환 */
    public function replaceItems(int $templateId, array $items): void
    {
        $tenantId = $this->tenantId();

        $tpl = BomTemplate::query()
            ->where('tenant_id', $tenantId)
            ->find($templateId);

        if (! $tpl) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        // 1차 검증
        foreach ($items as $i => $row) {
            $refType = strtoupper((string) Arr::get($row, 'ref_type'));
            $qty = (float) Arr::get($row, 'qty', 0);
            if (! in_array($refType, ['MATERIAL', 'PRODUCT'], true)) {
                throw ValidationException::withMessages(["items.$i.ref_type" => __('error.validation_failed')]);
            }
            if ($qty <= 0) {
                throw ValidationException::withMessages(["items.$i.qty" => __('error.validation_failed')]);
            }
        }

        DB::transaction(function () use ($tenantId, $tpl, $items) {
            // 이전 항목 스냅샷
            $beforeItems = BomTemplateItem::query()
                ->where('tenant_id', $tenantId)
                ->where('bom_template_id', $tpl->id)
                ->orderBy('sort_order')
                ->get()
                ->map(fn ($i) => [
                    'ref_type' => strtoupper($i->ref_type),
                    'ref_id' => (int) $i->ref_id,
                    'qty' => (float) $i->qty,
                    'waste_rate' => (float) $i->waste_rate,
                    'uom_id' => $i->uom_id,
                    'notes' => $i->notes,
                    'sort_order' => (int) $i->sort_order,
                ])->all();

            BomTemplateItem::query()
                ->where('tenant_id', $tenantId)
                ->where('bom_template_id', $tpl->id)
                ->delete();

            $now = now();
            $payloads = [];
            foreach ($items as $row) {
                $payloads[] = [
                    'tenant_id' => $tenantId,
                    'bom_template_id' => $tpl->id,
                    'ref_type' => strtoupper($row['ref_type']),
                    'ref_id' => (int) $row['ref_id'],
                    'qty' => (string) ($row['qty'] ?? 1),
                    'waste_rate' => (string) ($row['waste_rate'] ?? 0),
                    'uom_id' => $row['uom_id'] ?? null,
                    'notes' => $row['notes'] ?? null,
                    'sort_order' => (int) ($row['sort_order'] ?? 0),
                    'created_at' => $now,
                    'updated_at' => $now,
                ];
            }

            if (! empty($payloads)) {
                BomTemplateItem::insert($payloads);
            }

            // 감사 로그
            app(\App\Services\Audit\AuditLogger::class)->log(
                tenantId: $tenantId,
                targetType: 'bom_template',
                targetId: $tpl->id,
                action: 'items_replaced',
                before: ['items' => $beforeItems],
                after: ['items' => array_map(function ($p) {
                    return [
                        'ref_type' => $p['ref_type'],
                        'ref_id' => $p['ref_id'],
                        'qty' => (float) $p['qty'],
                        'waste_rate' => (float) $p['waste_rate'],
                        'uom_id' => $p['uom_id'],
                        'notes' => $p['notes'],
                        'sort_order' => $p['sort_order'],
                    ];
                }, $payloads)]
            );
        });
    }

    /** 항목 조회 */
    public function listItems(int $templateId)
    {
        $tenantId = $this->tenantId();

        $tpl = BomTemplate::query()
            ->where('tenant_id', $tenantId)
            ->find($templateId);

        if (! $tpl) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        return BomTemplateItem::query()
            ->where('tenant_id', $tenantId)
            ->where('bom_template_id', $tpl->id)
            ->orderBy('sort_order')
            ->get();
    }

    /** 템플릿 간 diff */
    public function diffTemplates(int $leftTemplateId, int $rightTemplateId): array
    {
        $tenantId = $this->tenantId();

        $left = BomTemplate::query()->where('tenant_id', $tenantId)->find($leftTemplateId);
        $right = BomTemplate::query()->where('tenant_id', $tenantId)->find($rightTemplateId);

        if (! $left || ! $right) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        $leftItems = BomTemplateItem::query()
            ->where('tenant_id', $tenantId)
            ->where('bom_template_id', $left->id)
            ->get()
            ->map(fn ($i) => [
                'key' => strtoupper($i->ref_type).':'.(int) $i->ref_id,
                'ref_type' => strtoupper($i->ref_type),
                'ref_id' => (int) $i->ref_id,
                'qty' => (float) $i->qty,
                'waste_rate' => (float) $i->waste_rate,
                'uom_id' => $i->uom_id ? (int) $i->uom_id : null,
                'notes' => $i->notes,
                'sort_order' => (int) $i->sort_order,
            ])->keyBy('key');

        $rightItems = BomTemplateItem::query()
            ->where('tenant_id', $tenantId)
            ->where('bom_template_id', $right->id)
            ->get()
            ->map(fn ($i) => [
                'key' => strtoupper($i->ref_type).':'.(int) $i->ref_id,
                'ref_type' => strtoupper($i->ref_type),
                'ref_id' => (int) $i->ref_id,
                'qty' => (float) $i->qty,
                'waste_rate' => (float) $i->waste_rate,
                'uom_id' => $i->uom_id ? (int) $i->uom_id : null,
                'notes' => $i->notes,
                'sort_order' => (int) $i->sort_order,
            ])->keyBy('key');

        $added = [];
        $removed = [];
        $changed = [];

        foreach ($rightItems as $key => $ri) {
            if (! $leftItems->has($key)) {
                $added[] = Arr::except($ri, ['key']);
            } else {
                $li = $leftItems[$key];
                $diffs = [];
                foreach (['qty', 'waste_rate', 'uom_id', 'notes', 'sort_order'] as $fld) {
                    if (($li[$fld] ?? null) !== ($ri[$fld] ?? null)) {
                        $diffs[$fld] = ['before' => $li[$fld] ?? null, 'after' => $ri[$fld] ?? null];
                    }
                }
                if (! empty($diffs)) {
                    $changed[] = [
                        'ref_type' => $ri['ref_type'],
                        'ref_id' => $ri['ref_id'],
                        'changes' => $diffs,
                    ];
                }
            }
        }
        foreach ($leftItems as $key => $li) {
            if (! $rightItems->has($key)) {
                $removed[] = Arr::except($li, ['key']);
            }
        }

        $result = [
            'left_template_id' => $left->id,
            'right_template_id' => $right->id,
            'summary' => [
                'added' => count($added),
                'removed' => count($removed),
                'changed' => count($changed),
            ],
            'added' => $added,
            'removed' => $removed,
            'changed' => $changed,
        ];

        if (config('audit.log_reads', false)) {
            app(\App\Services\Audit\AuditLogger::class)->log(
                tenantId: $tenantId,
                targetType: 'bom_template',
                targetId: $left->id,
                action: 'diff_viewed',
                before: ['left' => $left->id, 'right' => $right->id],
                after: ['summary' => $result['summary']]
            );
        }

        return $result;
    }

    /** 템플릿 복제(깊은 복사) */
    public function cloneTemplate(int $templateId, ?int $targetVersionId = null, ?string $name = null, bool $isPrimary = false, ?string $notes = null): BomTemplate
    {
        $tenantId = $this->tenantId();

        $src = BomTemplate::query()->where('tenant_id', $tenantId)->find($templateId);
        if (! $src) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        $targetVersionId = $targetVersionId ?: $src->model_version_id;

        $mv = ModelVersion::query()
            ->where('tenant_id', $tenantId)
            ->find($targetVersionId);

        if (! $mv) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        // 이름 결정(중복 회피)
        $baseName = $name ?: ($src->name.' Copy');
        $newName = $baseName;
        $i = 2;
        while (BomTemplate::query()
            ->where('model_version_id', $mv->id)
            ->where('name', $newName)
            ->exists()) {
            $newName = $baseName.' '.$i;
            $i++;
        }

        return DB::transaction(function () use ($tenantId, $src, $mv, $newName, $isPrimary, $notes) {
            $dest = BomTemplate::create([
                'tenant_id' => $tenantId,
                'model_version_id' => $mv->id,
                'name' => $newName,
                'is_primary' => $isPrimary,
                'notes' => $notes ?? $src->notes,
            ]);

            $now = now();
            $items = BomTemplateItem::query()
                ->where('tenant_id', $tenantId)
                ->where('bom_template_id', $src->id)
                ->get()
                ->map(fn ($i) => [
                    'tenant_id' => $tenantId,
                    'bom_template_id' => $dest->id,
                    'ref_type' => strtoupper($i->ref_type),
                    'ref_id' => (int) $i->ref_id,
                    'qty' => (string) $i->qty,
                    'waste_rate' => (string) $i->waste_rate,
                    'uom_id' => $i->uom_id,
                    'notes' => $i->notes,
                    'sort_order' => (int) $i->sort_order,
                    'created_at' => $now,
                    'updated_at' => $now,
                ])->all();

            if (! empty($items)) {
                BomTemplateItem::insert($items);
            }

            if ($isPrimary) {
                BomTemplate::query()
                    ->where('model_version_id', $mv->id)
                    ->where('id', '<>', $dest->id)
                    ->update(['is_primary' => false]);
            }

            // 감사 로그
            app(\App\Services\Audit\AuditLogger::class)->log(
                tenantId: $tenantId,
                targetType: 'bom_template',
                targetId: $dest->id,
                action: 'cloned',
                before: ['source_template_id' => $src->id],
                after: $dest->toArray()
            );

            return $dest;
        });
    }

    /** 모델버전 릴리즈 전 유효성 검사 */
    public function validateForRelease(int $modelVersionId): void
    {
        $tenantId = $this->tenantId();

        $mv = ModelVersion::query()
            ->where('tenant_id', $tenantId)
            ->find($modelVersionId);

        if (! $mv) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        // 대표 템플릿 존재 확인
        $primary = BomTemplate::query()
            ->where('tenant_id', $tenantId)
            ->where('model_version_id', $mv->id)
            ->where('is_primary', true)
            ->first();

        if (! $primary) {
            throw ValidationException::withMessages(['template' => __('error.validation_failed')]);
        }

        $items = BomTemplateItem::query()
            ->where('tenant_id', $tenantId)
            ->where('bom_template_id', $primary->id)
            ->orderBy('sort_order')
            ->get();

        if ($items->isEmpty()) {
            throw ValidationException::withMessages(['items' => __('error.validation_failed')]);
        }

        // 중복 키 및 값 검증
        $seen = [];
        foreach ($items as $idx => $it) {
            $key = strtoupper($it->ref_type).':'.(int) $it->ref_id;
            if (isset($seen[$key])) {
                throw ValidationException::withMessages(["items.$idx" => __('error.duplicate')]);
            }
            $seen[$key] = true;

            // 수량/로스율
            if ((float) $it->qty <= 0) {
                throw ValidationException::withMessages(["items.$idx.qty" => __('error.validation_failed')]);
            }
            if ((float) $it->waste_rate < 0) {
                throw ValidationException::withMessages(["items.$idx.waste_rate" => __('error.validation_failed')]);
            }

            // 참조 존재/활성/테넌트 일치
            if (strtoupper($it->ref_type) === 'MATERIAL') {
                $exists = Material::query()
                    ->where('tenant_id', $tenantId)
                    ->where('id', $it->ref_id)
                    ->whereNull('deleted_at')
                    ->exists();
                if (! $exists) {
                    throw ValidationException::withMessages(["items.$idx.ref_id" => __('error.not_found')]);
                }
            } elseif (strtoupper($it->ref_type) === 'PRODUCT') {
                $exists = Product::query()
                    ->where('tenant_id', $tenantId)
                    ->where('id', $it->ref_id)
                    ->whereNull('deleted_at')
                    ->exists();
                if (! $exists) {
                    throw ValidationException::withMessages(["items.$idx.ref_id" => __('error.not_found')]);
                }
            } else {
                throw ValidationException::withMessages(["items.$idx.ref_type" => __('error.validation_failed')]);
            }
        }

        // 주: 순환 참조 검사는 운영 BOM 실제 구성 시 그래프 탐색으로 보완 예정
    }
}
