<?php

namespace App\Services;

use App\Models\Contact;
use App\Models\Account;
use App\Models\ChartOfAccount;
use App\Models\Shop;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Exception;

class ContactService
{
    /**
     * Create a new contact with appropriate ledger integration
     */
    public function createContact(array $data): Contact
    {
        return DB::transaction(function () use ($data) {
            try {
                $contact = null;

                // Create contact based on type
                switch ($data['type']) {
                    case 'employee':
                        $contact = $this->createEmployeeContact($data);
                        break;
                    case 'supplier':
                        $contact = $this->createVendorContact($data);
                        break;
                    case 'customer':
                        $contact = $this->createCustomerContact($data);
                        break;
                    case 'shop_manager':
                        $contact = $this->createShopManager($data);
                        break;
                    case 'shop_employee':
                        $contact = $this->createShopEmployee($data);
                        break;
                    default:
                        throw new Exception("Invalid contact type: {$data['type']}");
                }

                Log::info('Contact created successfully', [
                    'contact_id' => $contact->id,
                    'contact_type' => $contact->type,
                    'name' => $contact->name
                ]);

                return $contact->fresh();

            } catch (Exception $e) {
                Log::error('Failed to create contact', [
                    'error' => $e->getMessage(),
                    'data' => $data
                ]);
                throw $e;
            }
        });
    }

    /**
     * Create employee contact
     */
    private function createEmployeeContact(array $data): Contact
    {
        // Create contact
        $contact = Contact::create($data);

        // Create employee ledger
        $employeeAccount = $this->createEmployeeAccount($contact, 'Employees');

        // Update contact with employee account
        $contact->update(['employee_account_id' => $employeeAccount->id]);

        // Create user if requested
        if (isset($data['create_user']) && $data['create_user']) {
            $this->createUserForContact($contact, $data);
        }

        return $contact;
    }

    /**
     * Create vendor contact
     */
    private function createVendorContact(array $data): Contact
    {
        // Create contact
        $contact = Contact::create($data);

        // Create vendor ledger
        $vendorAccount = $this->createVendorAccount($contact);

        // Update contact with account
        $contact->update(['account_id' => $vendorAccount->id]);

        return $contact;
    }

    /**
     * Create customer contact
     */
    private function createCustomerContact(array $data): Contact
    {
        // Create contact
        $contact = Contact::create($data);

        // Create customer ledger
        $customerAccount = $this->createCustomerAccount($contact);

        // Update contact with account
        $contact->update(['account_id' => $customerAccount->id]);

        return $contact;
    }

    /**
     * Create shop manager with dual-ledger integration
     */
    private function createShopManager(array $data): Contact
    {
        // Create contact
        $contact = Contact::create($data);

        // Create employee ledger for shop manager
        $employeeAccount = $this->createEmployeeAccount($contact, 'Shop Managers');

        // Create shop ledger
        $shopAccount = $this->createShopAccount($contact, $data);

        // Create shop record
        $shop = Shop::create([
            'contact_id' => $contact->id,
            'name' => $data['shop_name'],
            'phone' => $data['shop_phone'] ?? $contact->phone,
            'email' => $data['shop_email'] ?? $contact->email,
            'address' => $data['shop_address'] ?? $contact->address,
            'city' => $data['city'],
            'manager_contact_id' => $contact->id,
            'shop_account_id' => $shopAccount->id,
        ]);

        // Update contact with both accounts
        $contact->update([
            'employee_account_id' => $employeeAccount->id,
            'account_id' => $shopAccount->id,
        ]);

        // Create user if requested
        if (isset($data['create_user']) && $data['create_user']) {
            $this->createUserForContact($contact, $data);
        }

        Log::info('Shop manager created with dual-ledger', [
            'contact_id' => $contact->id,
            'shop_id' => $shop->id,
            'employee_account_id' => $employeeAccount->id,
            'shop_account_id' => $shopAccount->id,
        ]);

        return $contact;
    }

    /**
     * Create shop employee
     */
    private function createShopEmployee(array $data): Contact
    {
        // Validate shop exists
        $shop = Shop::findOrFail($data['shop_id']);

        // Create contact
        $contact = Contact::create($data);

        // Create employee ledger for shop employee
        $employeeAccount = $this->createEmployeeAccount($contact, 'Shop Employees');

        // Update contact with employee account and shop link
        $contact->update([
            'employee_account_id' => $employeeAccount->id,
            'shop_id' => $shop->id,
        ]);

        // Create user if requested
        if (isset($data['create_user']) && $data['create_user']) {
            $this->createUserForContact($contact, $data);
        }

        Log::info('Shop employee created', [
            'contact_id' => $contact->id,
            'shop_id' => $shop->id,
            'employee_account_id' => $employeeAccount->id,
        ]);

        return $contact;
    }

    /**
     * Create employee account
     */
    private function createEmployeeAccount(Contact $contact, string $subCategory = null): Account
    {
        // Get the Employee Accounts chart of account (code: 5211)
        $chartOfAccount = ChartOfAccount::where('code', '5211')->first();

        if (!$chartOfAccount) {
            throw new Exception('Employee Accounts chart of account not found. Please run the ChartOfAccountHierarchySeeder.');
        }

        return Account::create([
            'chart_of_account_id' => $chartOfAccount->id,
            'name' => $contact->name,
            'code' => 'EMP-' . $contact->id,
            'description' => 'Employee account for ' . $contact->name,
            'balance' => 0,
            'is_active' => true,
            'model_type' => 'contact',
            'model_id' => $contact->id,
        ]);
    }

    /**
     * Create vendor account
     */
    private function createVendorAccount(Contact $contact): Account
    {
        // Get the Vendor Accounts chart of account (code: 2111)
        $chartOfAccount = ChartOfAccount::where('code', '2111')->first();

        if (!$chartOfAccount) {
            throw new Exception('Vendor Accounts chart of account not found. Please run the ChartOfAccountHierarchySeeder.');
        }

        return Account::create([
            'chart_of_account_id' => $chartOfAccount->id,
            'name' => $contact->name,
            'code' => 'VEND-' . $contact->id,
            'description' => 'Vendor account for ' . $contact->name,
            'balance' => 0,
            'is_active' => true,
            'model_type' => 'contact',
            'model_id' => $contact->id,
        ]);
    }

    /**
     * Create customer account
     */
    private function createCustomerAccount(Contact $contact): Account
    {
        // Get the Customer Accounts chart of account (code: 1121)
        $chartOfAccount = ChartOfAccount::where('code', '1121')->first();

        if (!$chartOfAccount) {
            throw new Exception('Customer Accounts chart of account not found. Please run the ChartOfAccountHierarchySeeder.');
        }

        return Account::create([
            'chart_of_account_id' => $chartOfAccount->id,
            'name' => $contact->name,
            'code' => 'CUST-' . $contact->id,
            'description' => 'Customer account for ' . $contact->name,
            'balance' => 0,
            'is_active' => true,
            'model_type' => 'contact',
            'model_id' => $contact->id,
        ]);
    }

    /**
     * Create shop account
     */
    private function createShopAccount(Contact $contact, array $data): Account
    {
        // Get the Shop Operations chart of account (code: 5220)
        $chartOfAccount = ChartOfAccount::where('code', '5220')->first();

        if (!$chartOfAccount) {
            throw new Exception('Shop Operations chart of account not found. Please run the ChartOfAccountHierarchySeeder.');
        }

        return Account::create([
            'chart_of_account_id' => $chartOfAccount->id,
            'name' => $data['shop_name'],
            'code' => 'SHOP-' . $contact->id,
            'description' => 'Shop operations account for ' . $data['shop_name'],
            'balance' => 0,
            'is_active' => true,
            'model_type' => 'contact',
            'model_id' => $contact->id,
        ]);
    }

    /**
     * Get or create chart of account
     */
    private function getOrCreateChartOfAccount(string $code): ChartOfAccount
    {
        return ChartOfAccount::firstOrCreate(
            ['code' => $code],
            [
                'name' => ucfirst(strtolower($code)),
                'description' => ucfirst(strtolower($code)) . ' accounts'
            ]
        );
    }

    /**
     * Create user for contact
     */
    private function createUserForContact(Contact $contact, array $data): void
    {
        $user = User::create([
            'name' => $contact->name,
            'email' => $contact->email,
            'username' => $data['username'],
            'password' => Hash::make($data['password']),
            'is_active' => $contact->is_active
        ]);

        // Assign role if specified
        if (isset($data['role_id']) && $data['role_id']) {
            $role = \Spatie\Permission\Models\Role::find($data['role_id']);
            if ($role) {
                $user->assignRole($role);
            }
        }

        // Update contact with user_id
        $contact->update(['user_id' => $user->id]);

        Log::info('User account created for contact', [
            'contact_id' => $contact->id,
            'user_id' => $user->id,
            'username' => $data['username']
        ]);
    }

    /**
     * Update an existing contact
     */
    public function updateContact(Contact $contact, array $data): Contact
    {
        return DB::transaction(function () use ($contact, $data) {
            try {
                $oldType = $contact->type;
                $oldData = $contact->toArray();

                // Update the contact
                $contact->update($data);

                // Handle type-specific updates
                if ($contact->type === 'shop_manager') {
                    $this->updateShopManagerSetup($contact, $data);
                } elseif ($contact->type === 'shop_employee') {
                    $this->updateShopEmployeeSetup($contact, $data);
                }

                // Handle user creation if requested
                if (isset($data['create_user']) && $data['create_user'] && !$contact->user_id) {
                    $this->createUserForContact($contact, $data);
                }

                Log::info('Contact updated successfully', [
                    'contact_id' => $contact->id,
                    'contact_type' => $contact->type,
                    'old_type' => $oldType
                ]);

                return $contact->fresh();

            } catch (Exception $e) {
                Log::error('Failed to update contact', [
                    'error' => $e->getMessage(),
                    'contact_id' => $contact->id,
                    'data' => $data
                ]);
                throw $e;
            }
        });
    }

    /**
     * Update shop manager setup
     */
    private function updateShopManagerSetup(Contact $contact, array $data): void
    {
        // Update or create shop record
        $shop = $contact->managedShop;
        if (!$shop) {
            $shop = new Shop(['manager_contact_id' => $contact->id]);
        }

        $shop->fill([
            'contact_id' => $contact->id,
            'name' => $data['shop_name'] ?? $contact->name,
            'phone' => $data['shop_phone'] ?? $contact->phone,
            'email' => $data['shop_email'] ?? $contact->email,
            'address' => $data['shop_address'] ?? $contact->address,
            'city' => $data['city'] ?? 'Unknown'
        ]);

        $shop->save();

        Log::info('Shop manager setup updated', [
            'contact_id' => $contact->id,
            'shop_name' => $shop->name,
            'city' => $shop->city
        ]);
    }

    /**
     * Update shop employee setup
     */
    private function updateShopEmployeeSetup(Contact $contact, array $data): void
    {
        // Update shop assignment if changed
        if (isset($data['shop_id']) && $data['shop_id'] != $contact->shop_id) {
            $shop = Shop::findOrFail($data['shop_id']);
            $contact->update(['shop_id' => $shop->id]);

            Log::info('Shop employee shop assignment updated', [
                'contact_id' => $contact->id,
                'old_shop_id' => $contact->shop_id,
                'new_shop_id' => $shop->id
            ]);
        }
    }

    /**
     * Enhance contact with additional setup like user creation, shop relationships, etc.
     * Used after Account is created and Contact is linked to it.
     */
    public function enhanceContact(Contact $contact, array $data): Contact
    {
        return DB::transaction(function () use ($contact, $data) {
            try {
                // Handle type-specific enhancements
                switch ($contact->type) {
                    case 'employee':
                        $this->enhanceEmployeeContact($contact, $data);
                        break;
                    case 'shop_manager':
                        $this->enhanceShopManagerContact($contact, $data);
                        break;
                    case 'shop_employee':
                        $this->enhanceShopEmployeeContact($contact, $data);
                        break;
                    case 'supplier':
                    case 'customer':
                        // No additional enhancement needed for suppliers/customers
                        break;
                }

                // Create user if requested
                if (isset($data['create_user']) && $data['create_user']) {
                    $this->createUserForContact($contact, $data);
                }

                Log::info('Contact enhanced successfully', [
                    'contact_id' => $contact->id,
                    'contact_type' => $contact->type,
                    'name' => $contact->name
                ]);

                return $contact->fresh();

            } catch (Exception $e) {
                Log::error('Failed to enhance contact', [
                    'error' => $e->getMessage(),
                    'contact_id' => $contact->id,
                    'data' => $data
                ]);
                throw $e;
            }
        });
    }

    /**
     * Enhance employee contact with employee account link
     */
    private function enhanceEmployeeContact(Contact $contact, array $data): void
    {
        // Create employee account link if needed
        if (!$contact->employee_account_id) {
            $employeeAccount = $this->createEmployeeAccount($contact, 'Employees');
            $contact->update(['employee_account_id' => $employeeAccount->id]);
        }
    }

    /**
     * Enhance shop manager contact with shop and dual account setup
     */
    private function enhanceShopManagerContact(Contact $contact, array $data): void
    {
        // Create employee account link if needed
        if (!$contact->employee_account_id) {
            $employeeAccount = $this->createEmployeeAccount($contact, 'Shop Managers');
            $contact->update(['employee_account_id' => $employeeAccount->id]);
        }

        // Create shop if provided in data
        if (isset($data['shop_name']) && $data['shop_name']) {
            $shopAccount = $this->createShopAccount($contact, $data);

            $shop = Shop::create([
                'contact_id' => $contact->id,
                'name' => $data['shop_name'],
                'phone' => $data['shop_phone'] ?? $contact->phone,
                'email' => $data['shop_email'] ?? $contact->email,
                'address' => $data['shop_address'] ?? $contact->address,
                'city' => $data['city'] ?? 'Unknown',
                'manager_contact_id' => $contact->id,
                'shop_account_id' => $shopAccount->id,
            ]);

            Log::info('Shop created for manager', [
                'contact_id' => $contact->id,
                'shop_id' => $shop->id,
                'shop_account_id' => $shopAccount->id,
            ]);
        }
    }

    /**
     * Enhance shop employee contact with shop assignment and employee account
     */
    private function enhanceShopEmployeeContact(Contact $contact, array $data): void
    {
        // Validate and assign shop if provided
        if (isset($data['shop_id']) && $data['shop_id']) {
            $shop = Shop::findOrFail($data['shop_id']);
            $contact->update(['shop_id' => $shop->id]);
        }

        // Create employee account link if needed
        if (!$contact->employee_account_id) {
            $employeeAccount = $this->createEmployeeAccount($contact, 'Shop Employees');
            $contact->update(['employee_account_id' => $employeeAccount->id]);
        }
    }
}
