<?php

namespace App\Services;

use App\Models\Company;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class CompanyDataResetService
{
    /** @var list<string> */
    public const TRANSACTION_TABLES = [
        'payment_allocations',
        'bill_payments',
        'bank_reconciliations',
        'ledger_entries',
        'voucher_lines',
        'voucher_approvals',
        'transaction_attachments',
        'opening_balances',
        'stock_movements',
        'item_serial_numbers',
        'item_lot_numbers',
        'bom_lines',
        'budget_lines',
        'purchase_requisitions',
        'purchase_orders',
        'goods_receipts',
        'grns',
        'quotations',
        'sales_orders',
        'delivery_notes',
        'sales_invoices',
        'sales_returns',
        'receive_payments',
        'bills',
        'boms',
        'production_orders',
        'budgets',
        'vouchers',
        'audit_logs',
    ];

    /** @var list<string> */
    public const LISTING_TABLES = [
        'item_price_levels',
        'items',
        'item_categories',
        'warehouses',
        'uoms',
        'customers',
        'vendors',
        'employees',
        'other_names',
        'bank_accounts',
        'accounts',
        'dimension_values',
        'dimensions',
        'price_levels',
        'tax_codes',
        'exchange_rates',
        'accounting_periods',
    ];

    /**
     * @return array<string, int>
     */
    public function deleteTransactions(Company $company): array
    {
        return $this->deleteFromTables($company, self::TRANSACTION_TABLES);
    }

    /**
     * @return array<string, int>
     */
    public function deleteListings(Company $company): array
    {
        if ($this->hasRemainingTransactions($company)) {
            throw new \RuntimeException(
                'Delete all transactions first before removing listings.'
            );
        }

        return $this->deleteFromTables($company, self::LISTING_TABLES);
    }

    public function hasRemainingTransactions(Company $company): bool
    {
        foreach (self::TRANSACTION_TABLES as $table) {
            if ($table === 'audit_logs') {
                continue;
            }

            if (!Schema::hasTable($table) || !Schema::hasColumn($table, 'company_id')) {
                continue;
            }

            if (DB::table($table)->where('company_id', $company->id)->exists()) {
                return true;
            }
        }

        return false;
    }

    /**
     * @return array<string, int> table => row count
     */
    public function countTransactions(Company $company): array
    {
        return $this->countTables($company, self::TRANSACTION_TABLES);
    }

    /**
     * @return array<string, int> table => row count
     */
    public function countListings(Company $company): array
    {
        return $this->countTables($company, self::LISTING_TABLES);
    }

    /**
     * @param  list<string>  $tables
     * @return array<string, int>
     */
    private function deleteFromTables(Company $company, array $tables): array
    {
        return DB::transaction(function () use ($company, $tables) {
            Schema::disableForeignKeyConstraints();

            $deleted = [];
            foreach ($tables as $table) {
                if (!Schema::hasTable($table) || !Schema::hasColumn($table, 'company_id')) {
                    continue;
                }

                $count = DB::table($table)->where('company_id', $company->id)->delete();
                if ($count > 0) {
                    $deleted[$table] = $count;
                }
            }

            Schema::enableForeignKeyConstraints();

            return $deleted;
        });
    }

    /**
     * @param  list<string>  $tables
     * @return array<string, int>
     */
    private function countTables(Company $company, array $tables): array
    {
        $counts = [];

        foreach ($tables as $table) {
            if (!Schema::hasTable($table) || !Schema::hasColumn($table, 'company_id')) {
                continue;
            }

            $count = DB::table($table)->where('company_id', $company->id)->count();
            if ($count > 0) {
                $counts[$table] = $count;
            }
        }

        return $counts;
    }
}
