<?php

namespace App\Http\Controllers\Company\Admin;

use App\Http\Controllers\Controller;
use App\Imports\QuickBooks\QuickBooksCoaImport;
use App\Imports\QuickBooks\QuickBooksCustomersImport;
use App\Imports\QuickBooks\QuickBooksEmployeesImport;
use App\Imports\QuickBooks\QuickBooksOtherNamesImport;
use App\Imports\QuickBooks\QuickBooksVendorsImport;
use App\Models\AuditLog;
use App\Support\QuickBooksSheetResolver;
use App\Services\QuickBooks\QuickBooksJournalBulkImporter;
use App\Services\QuickBooks\QuickBooksJournalParser;
use App\Services\QuickBooks\QuickBooksMissingMasterCreator;
use App\Services\QuickBooks\QuickBooksReferenceRegistry;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class QuickBooksImportController extends Controller
{
    private const LIST_IMPORT_TABS = [
        'coa' => [
            'label'       => 'COA',
            'icon'        => 'bi-journal-text',
            'description' => 'Import chart of accounts from QuickBooks Desktop export.',
            'sample_file' => 'COA.xlsx',
            'permission'  => ['accounts', 'create'],
            'columns'     => ['Account', 'Type', 'Currency', 'Balance Total', 'Description', 'Tax Line'],
            'targets'     => ['accounts.name', 'accounts.type', 'accounts.detail_type', 'accounts.description'],
        ],
        'customers' => [
            'label'       => 'Customers: Job',
            'icon'        => 'bi-people',
            'description' => 'Import customers and jobs from QuickBooks Desktop export.',
            'sample_file' => 'Customer__Job.xlsx',
            'permission'  => ['ar', 'create'],
            'columns'     => ['Customer', 'Bill to', 'Company'],
            'targets'     => ['customers.name', 'customers.billing_address', 'customers.parent_id / is_job'],
        ],
        'vendors' => [
            'label'       => 'Vendors',
            'icon'        => 'bi-truck',
            'description' => 'Import vendors from QuickBooks Desktop export.',
            'sample_file' => 'Vendor.xlsx',
            'permission'  => ['ap', 'create'],
            'columns'     => ['Active Status', 'Vendor', 'Bill from', 'First Name', 'Last Name', 'Main Phone', 'Work Phone', 'Currency', 'Vendor Type'],
            'targets'     => ['vendors.name', 'vendors.address', 'vendors.contact_person', 'vendors.phone', 'vendors.currency_id', 'vendors.is_active'],
        ],
        'employees' => [
            'label'       => 'Employees',
            'icon'        => 'bi-person-badge',
            'description' => 'Import employees from QuickBooks Desktop export.',
            'sample_file' => 'Emloyee.xlsx',
            'permission'  => ['masters', 'create'],
            'columns'     => ['Active Status', 'Employee', 'Main Email', 'First Name', 'Last Name', 'Main Phone', 'Address'],
            'targets'     => ['employees.name', 'employees.email', 'employees.phone', 'employees.is_active'],
        ],
        'other-names' => [
            'label'       => 'Other Names',
            'icon'        => 'bi-person-lines-fill',
            'description' => 'Import other names from QuickBooks Desktop export.',
            'sample_file' => 'Other_Name.xlsx',
            'permission'  => ['masters', 'create'],
            'columns'     => ['Other Name', 'Address'],
            'targets'     => ['other_names.name', 'other_names.address'],
        ],
    ];

    private const DATA_IMPORT_TYPES = [
        'journal-entry' => [
            'label'       => 'Journal Entry',
            'icon'        => 'bi-journal-bookmark',
            'description' => 'Import QuickBooks journal entries as posted journal vouchers with ledger entries.',
            'sample_file' => 'JE.xlsx',
            'permission'  => ['vouchers', 'create'],
            'columns'     => ['Trans #', 'Date', 'Num', 'Name', 'Memo', 'Account', 'Debit', 'Credit', 'Account Type'],
            'targets'     => ['vouchers', 'voucher_lines', 'ledger_entries', 'accounts.account_type on auto-create'],
        ],
    ];

    public function listImport(Request $request)
    {
        $activeTab = $request->query('tab', 'coa');

        if (!array_key_exists($activeTab, self::LIST_IMPORT_TABS)) {
            $activeTab = 'coa';
        }

        return view('admin.quickbooks.list-import', [
            'tabs'      => self::LIST_IMPORT_TABS,
            'activeTab' => $activeTab,
        ]);
    }

    public function importList(Request $request, string $tab)
    {
        $config = self::LIST_IMPORT_TABS[$tab] ?? abort(404);
        $this->gate($config['permission'][0], $config['permission'][1]);

        $validated = $request->validate([
            'import_file' => 'required|file|mimes:xlsx,xls,csv|max:10240',
        ]);

        $companyId = auth()->user()->company_id;
        $sheetName = QuickBooksSheetResolver::dataSheetName($validated['import_file']->getRealPath());

        $import = match ($tab) {
            'coa'         => new QuickBooksCoaImport($companyId, $sheetName),
            'customers'   => new QuickBooksCustomersImport($companyId, $sheetName),
            'vendors'     => new QuickBooksVendorsImport($companyId, $sheetName),
            'employees'   => new QuickBooksEmployeesImport($companyId, $sheetName),
            'other-names' => new QuickBooksOtherNamesImport($companyId, $sheetName),
            default       => abort(404),
        };

        try {
            Excel::import($import, $validated['import_file']);
        } catch (\Throwable $e) {
            report($e);

            return redirect()
                ->route('admin.quickbooks.list-import', ['tab' => $tab])
                ->withErrors([
                    'import' => 'Import failed: ' . $e->getMessage(),
                ]);
        }

        $stats = $import->sheet ?? $import;
        $message = sprintf(
            '%s import completed: %d created, %d updated, %d skipped.',
            $config['label'],
            $stats->created,
            $stats->updated,
            $stats->skipped
        );

        AuditLog::record('import', $tab, null, null, [
            'created' => $stats->created,
            'updated' => $stats->updated,
            'skipped' => $stats->skipped,
            'errors'  => count($stats->errors),
        ], "QuickBooks {$config['label']} list imported");

        $redirect = redirect()
            ->route('admin.quickbooks.list-import', ['tab' => $tab])
            ->with('success', $message);

        if ($stats->errors) {
            $redirect->withErrors([
                'import' => implode(' ', array_slice($stats->errors, 0, 5)),
            ]);
        }

        return $redirect;
    }

    public function downloadSample(string $tab): BinaryFileResponse
    {
        $config = self::LIST_IMPORT_TABS[$tab] ?? abort(404);
        $path = storage_path('app/samples/quickbooks/' . $config['sample_file']);

        abort_unless(File::exists($path), 404, 'Sample file not found.');

        return response()->download($path, $config['sample_file']);
    }

    public function dataImport()
    {
        return view('admin.quickbooks.data-import', [
            'types'   => self::DATA_IMPORT_TYPES,
            'preview' => session('qb_data_preview'),
        ]);
    }

    public function analyzeDataImport(Request $request)
    {
        $this->gate('vouchers', 'create');

        $request->validate([
            'import_file' => 'required|file|mimes:xlsx,xls|max:51200',
            'data_type'   => 'required|in:journal-entry',
        ]);

        $token = (string) Str::uuid();
        $storedPath = $request->file('import_file')->storeAs(
            "quickbooks-imports/{$token}",
            'source.xlsx'
        );

        $fullPath = storage_path('app/' . $storedPath);
        $parser = new QuickBooksJournalParser();
        $transactions = $parser->parse($fullPath);
        $lineCount = array_sum(array_map(fn(array $txn) => count($txn['lines']), $transactions));

        $registry = new QuickBooksReferenceRegistry(auth()->user()->company_id);
        $missing = $registry->missingFromTransactions($transactions);

        if ($old = session('qb_data_preview')) {
            $this->deleteStoredImport($old['token'] ?? null);
        }

        session([
            'qb_data_preview' => [
                'token'              => $token,
                'path'               => $storedPath,
                'data_type'          => $request->input('data_type'),
                'transactions'       => count($transactions),
                'lines'              => $lineCount,
                'missing_accounts'   => $missing['accounts'],
                'missing_parties'    => $missing['parties'],
                'file_name'          => $request->file('import_file')->getClientOriginalName(),
            ],
        ]);

        return redirect()
            ->route('admin.quickbooks.data-import')
            ->with('success', 'File analyzed. Review missing lists below, then run the import.');
    }

    public function processDataImport(Request $request)
    {
        $this->gate('vouchers', 'create');

        $preview = session('qb_data_preview');
        abort_unless($preview && !empty($preview['path']), 422, 'Please upload and analyze a QuickBooks file first.');

        $request->validate([
            'create_missing_accounts' => 'nullable|boolean',
            'create_missing_parties'  => 'nullable|boolean',
            'party_default_type'      => 'nullable|in:customer,vendor,employee,other_name',
            'name_prefix'             => 'nullable|string|max:100',
        ]);

        $fullPath = storage_path('app/' . $preview['path']);
        abort_unless(File::exists($fullPath), 422, 'Uploaded file has expired. Please analyze the file again.');

        $companyId = auth()->user()->company_id;
        $parser = new QuickBooksJournalParser();
        $transactions = $parser->parse($fullPath);
        $registry = new QuickBooksReferenceRegistry($companyId);
        $missing = $registry->missingFromTransactions($transactions);

        $createAccounts = $request->boolean('create_missing_accounts');
        $createParties = $request->boolean('create_missing_parties');

        if ($missing['accounts'] && !$createAccounts) {
            return back()->withErrors([
                'import' => 'Some accounts are missing in your chart of accounts. Enable auto-create or import them from QuickBooks List Import first.',
            ]);
        }

        if ($missing['parties'] && !$createParties) {
            return back()->withErrors([
                'import' => 'Some customer/vendor/employee/other name records are missing. Enable auto-create or import them from QuickBooks List Import first.',
            ]);
        }

        $created = ['accounts' => 0, 'parties' => 0];
        if ($createAccounts || $createParties) {
            $creator = new QuickBooksMissingMasterCreator($companyId, $registry);
            $created = $creator->create(
                $createAccounts ? $missing['accounts'] : [],
                $createParties ? $missing['parties'] : [],
                $request->input('name_prefix', 'QB Import'),
                $request->input('party_default_type', 'customer'),
            );
        }

        $stillMissing = $registry->missingFromTransactions($transactions);
        if ($stillMissing['accounts'] || $stillMissing['parties']) {
            return back()->withErrors([
                'import' => 'Some references are still missing after the auto-create step. Please import the related lists first.',
            ]);
        }

        $result = (new QuickBooksJournalBulkImporter($companyId, auth()->id(), $registry))
            ->import($transactions);

        $this->deleteStoredImport($preview['token']);
        session()->forget('qb_data_preview');

        $message = sprintf(
            'Journal import completed: %d vouchers imported, %d skipped, %d accounts auto-created, %d parties auto-created.',
            $result['imported'],
            $result['skipped'],
            $created['accounts'],
            $created['parties']
        );

        $redirect = redirect()
            ->route('admin.quickbooks.data-import')
            ->with('success', $message);

        if (!empty($result['errors'])) {
            $redirect->withErrors([
                'import' => implode(' ', array_slice($result['errors'], 0, 5)),
            ]);
        }

        return $redirect;
    }

    public function downloadDataSample(string $type): BinaryFileResponse
    {
        $config = self::DATA_IMPORT_TYPES[$type] ?? abort(404);
        $path = storage_path('app/samples/quickbooks/' . $config['sample_file']);

        abort_unless(File::exists($path), 404, 'Sample file not found.');

        return response()->download($path, $config['sample_file']);
    }

    public function cancelDataImport()
    {
        if ($preview = session('qb_data_preview')) {
            $this->deleteStoredImport($preview['token'] ?? null);
            session()->forget('qb_data_preview');
        }

        return redirect()
            ->route('admin.quickbooks.data-import')
            ->with('success', 'Import preview cleared.');
    }

    private function deleteStoredImport(?string $token): void
    {
        if (!$token) {
            return;
        }

        File::deleteDirectory(storage_path('app/quickbooks-imports/' . $token));
    }

    private function gate(string $module, string $action): void
    {
        if (!auth()->user()->hasPermission($module, $action)) {
            abort(403, 'You do not have permission to run this import.');
        }
    }
}
