<?php

namespace App\Imports\QuickBooks;

use App\Imports\QuickBooks\Concerns\InteractsWithQuickBooksRows;
use App\Imports\QuickBooks\Concerns\SkipsQuickBooksExtraSheets;
use App\Models\Customer;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\SkipsUnknownSheets;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;

class QuickBooksCustomersImport implements WithMultipleSheets, SkipsUnknownSheets
{
    use SkipsQuickBooksExtraSheets;

    public readonly QuickBooksCustomersSheetImport $sheet;

    private readonly string $sheetName;

    public function __construct(int $companyId, string $sheetName = 'Sheet1')
    {
        $this->sheetName = $sheetName;
        $this->sheet = new QuickBooksCustomersSheetImport($companyId);
    }

    public function sheets(): array
    {
        return [$this->sheetName => $this->sheet];
    }
}

class QuickBooksCustomersSheetImport implements ToCollection, WithHeadingRow
{
    use InteractsWithQuickBooksRows;

    public int $created = 0;
    public int $updated = 0;
    public int $skipped = 0;
    public array $errors = [];

    /** @var array<string, int> */
    private array $customerIdsByName = [];

    public function __construct(private readonly int $companyId)
    {
    }

    public function collection(Collection $rows): void
    {
        $parsed = [];

        foreach ($rows as $offset => $row) {
            $normalized = $this->normalizeRow($row);
            $customer = $this->clean($this->value($normalized, ['customer']));
            $company = $this->clean($this->value($normalized, ['company']));
            $billTo = $this->clean($this->value($normalized, ['bill to']));

            if ($customer === '' && $company === '') {
                continue;
            }

            $parsed[] = [
                'line' => $offset + 2,
                'customer' => $customer,
                'company' => $company !== '' ? $company : $customer,
                'bill_to' => $billTo,
                'is_job' => $company !== '' && strcasecmp($customer, $company) !== 0,
            ];
        }

        foreach ($parsed as $row) {
            if ($row['is_job']) {
                continue;
            }

            $this->upsertCustomer(
                $row['line'],
                $row['company'],
                $row['bill_to'],
                false,
                null,
                $row['customer'] !== $row['company'] ? $row['customer'] : null
            );
        }

        foreach ($parsed as $row) {
            if (!$row['is_job']) {
                continue;
            }

            $parentId = $this->customerIdsByName[strtolower($row['company'])] ?? null;

            if (!$parentId) {
                $this->errors[] = "Row {$row['line']}: Parent customer '{$row['company']}' was not found for job '{$row['customer']}'.";
                continue;
            }

            $this->upsertCustomer(
                $row['line'],
                $row['customer'],
                $row['bill_to'],
                true,
                $parentId,
                ctype_digit($row['customer']) ? $row['customer'] : null
            );
        }
    }

    private function upsertCustomer(
        int $line,
        string $name,
        string $billTo,
        bool $isJob,
        ?int $parentId,
        ?string $code
    ): void {
        $query = Customer::withoutGlobalScopes()
            ->where('company_id', $this->companyId)
            ->whereNull('deleted_at')
            ->where('name', $name);

        if ($isJob && $parentId) {
            $query->where('parent_id', $parentId);
        } else {
            $query->whereNull('parent_id');
        }

        $customer = $query->first();
        $isNew = !$customer;
        $customer ??= new Customer(['company_id' => $this->companyId]);

        $customer->fill([
            'parent_id' => $parentId,
            'is_job' => $isJob,
            'code' => $code,
            'name' => $name,
            'billing_address' => $billTo ?: null,
            'shipping_address' => $billTo ?: null,
            'is_active' => true,
            'depth' => $parentId ? 1 : 0,
            'path' => $parentId ? (string) $parentId : null,
        ]);

        try {
            $customer->save();

            if (!$isJob) {
                $this->customerIdsByName[strtolower($name)] = $customer->id;
            }

            $isNew ? $this->created++ : $this->updated++;
        } catch (\Throwable $e) {
            $this->errors[] = "Row {$line}: {$e->getMessage()}";
        }
    }
}
