<?php

namespace App\Http\Controllers;

use App\Models\BankAccount;
use App\Models\BankTransaction;
use App\Models\Cheque;
use App\Models\JournalEntry;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class ChequeController extends Controller
{
    /**
     * Display a listing of the cheques.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        $this->authorize('view_cheques');

        $query = Cheque::with(['bankAccount', 'bankAccount.bank']);

        // Apply filters if provided
        if ($request->has('account_id') && $request->account_id) {
            $query->where('bank_account_id', $request->account_id);
        }

        if ($request->has('type') && $request->type) {
            $query->where('cheque_type', $request->type);
        }

        if ($request->has('status') && $request->status) {
            $query->where('status', $request->status);
        }

        if ($request->has('start_date') && $request->start_date) {
            $query->where('cheque_date', '>=', $request->start_date);
        }

        if ($request->has('end_date') && $request->end_date) {
            $query->where('cheque_date', '<=', $request->end_date);
        }

        $cheques = $query->orderBy('cheque_date', 'desc')
            ->orderBy('id', 'desc')
            ->paginate(15);

        $bankAccounts = BankAccount::where('is_active', true)->get();

        return view('banking.cheques.index', compact('cheques', 'bankAccounts'));
    }

    /**
     * Show the form for creating a new incoming cheque.
     *
     * @return \Illuminate\Http\Response
     */
    public function createIncoming()
    {
        $this->authorize('create_cheques');

        $bankAccounts = BankAccount::where('is_active', true)->get();

        return view('banking.cheques.create_incoming', compact('bankAccounts'));
    }

    /**
     * Show the form for creating a new outgoing cheque.
     *
     * @return \Illuminate\Http\Response
     */
    public function createOutgoing()
    {
        $this->authorize('create_cheques');

        $bankAccounts = BankAccount::where('is_active', true)->get();

        return view('banking.cheques.create_outgoing', compact('bankAccounts'));
    }

    /**
     * Store a newly created incoming cheque in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function storeIncoming(Request $request)
    {
        $this->authorize('create_cheques');

        $validated = $request->validate([
            'bank_account_id' => 'required|exists:bank_accounts,id',
            'cheque_number' => 'required|string|max:50',
            'cheque_date' => 'required|date',
            'amount' => 'required|numeric|min:0.01',
            'payee_name' => 'required|string|max:255',
            'payee_contact' => 'nullable|string|max:255',
            'description' => 'nullable|string',
            'due_date' => 'required|date|after_or_equal:cheque_date',
            'attachment' => 'nullable|file|mimes:pdf,jpg,jpeg,png|max:2048',
        ]);

        try {
            DB::beginTransaction();

            // Create the cheque
            $cheque = new Cheque();
            $cheque->bank_account_id = $validated['bank_account_id'];
            $cheque->cheque_type = 'incoming';
            $cheque->cheque_number = $validated['cheque_number'];
            $cheque->cheque_date = $validated['cheque_date'];
            $cheque->amount = $validated['amount'];
            $cheque->payee_name = $validated['payee_name'];
            $cheque->payee_contact = $validated['payee_contact'] ?? null;
            $cheque->description = $validated['description'] ?? null;
            $cheque->due_date = $validated['due_date'];
            $cheque->status = 'pending';

            // Handle attachment if provided
            if ($request->hasFile('attachment')) {
                $path = $request->file('attachment')->store('cheques', 'public');
                $cheque->attachment = $path;
            }

            $cheque->save();

            // Log activity
            activity()
                ->performedOn($cheque)
                ->causedBy(auth()->user())
                ->withProperties(['cheque_id' => $cheque->id])
                ->log('Created incoming cheque: ' . $cheque->cheque_number);

            DB::commit();

            return redirect()->route('cheques.index')
                ->with('success', 'Incoming cheque created successfully.');
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Error creating incoming cheque: ' . $e->getMessage());

            return redirect()->back()
                ->withInput()
                ->with('error', 'Error creating incoming cheque. Please try again.');
        }
    }

    /**
     * Store a newly created outgoing cheque in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function storeOutgoing(Request $request)
    {
        $this->authorize('create_cheques');

        $validated = $request->validate([
            'bank_account_id' => 'required|exists:bank_accounts,id',
            'cheque_number' => 'required|string|max:50',
            'cheque_date' => 'required|date',
            'amount' => 'required|numeric|min:0.01',
            'payee_name' => 'required|string|max:255',
            'payee_contact' => 'nullable|string|max:255',
            'description' => 'nullable|string',
            'due_date' => 'required|date|after_or_equal:cheque_date',
            'attachment' => 'nullable|file|mimes:pdf,jpg,jpeg,png|max:2048',
        ]);

        try {
            DB::beginTransaction();

            $bankAccount = BankAccount::findOrFail($validated['bank_account_id']);

            // Check if bank account has sufficient balance
            if ($bankAccount->current_balance < $validated['amount']) {
                return redirect()->back()
                    ->withInput()
                    ->with('error', 'Insufficient balance in bank account.');
            }

            // Create the cheque
            $cheque = new Cheque();
            $cheque->bank_account_id = $validated['bank_account_id'];
            $cheque->cheque_type = 'outgoing';
            $cheque->cheque_number = $validated['cheque_number'];
            $cheque->cheque_date = $validated['cheque_date'];
            $cheque->amount = $validated['amount'];
            $cheque->payee_name = $validated['payee_name'];
            $cheque->payee_contact = $validated['payee_contact'] ?? null;
            $cheque->description = $validated['description'] ?? null;
            $cheque->due_date = $validated['due_date'];
            $cheque->status = 'issued';

            // Handle attachment if provided
            if ($request->hasFile('attachment')) {
                $path = $request->file('attachment')->store('cheques', 'public');
                $cheque->attachment = $path;
            }

            $cheque->save();

            // Update bank account balance
            $bankAccount->current_balance -= $validated['amount'];
            $bankAccount->save();

            // Create bank transaction
            $transaction = new BankTransaction();
            $transaction->bank_account_id = $validated['bank_account_id'];
            $transaction->transaction_type = 'cheque_payment';
            $transaction->transaction_date = $validated['cheque_date'];
            $transaction->amount = $validated['amount'];
            $transaction->reference_number = $validated['cheque_number'];
            $transaction->description = 'Cheque Payment to ' . $validated['payee_name'] . ': ' . ($validated['description'] ?? '');
            $transaction->cheque_id = $cheque->id;

            if ($request->hasFile('attachment')) {
                $transaction->attachment = $path;
            }

            $transaction->save();

            // Create journal entry
            $journalEntry = new JournalEntry();
            $journalEntry->entry_date = $validated['cheque_date'];
            $journalEntry->reference_number = 'CHQ-' . $validated['cheque_number'];
            $journalEntry->description = 'Cheque Payment: ' . ($validated['description'] ?? '');
            $journalEntry->status = 'posted';
            $journalEntry->created_by = auth()->id();
            $journalEntry->save();

            // Add journal entry items
            // Credit bank account
            $journalEntry->items()->create([
                'chart_of_account_id' => $bankAccount->chart_of_account_id,
                'description' => 'Cheque Payment from ' . $bankAccount->account_name,
                'debit' => 0,
                'credit' => $validated['amount'],
            ]);

            // Debit accounts payable or expense account
            // For simplicity, we're using a generic expense account here
            // In a real implementation, you would select the appropriate account
            $journalEntry->items()->create([
                'chart_of_account_id' => 50, // Assuming 50 is the ID of the Accounts Payable account
                'description' => 'Payment to ' . $validated['payee_name'],
                'debit' => $validated['amount'],
                'credit' => 0,
            ]);

            // Link transaction to journal entry
            $transaction->journal_entry_id = $journalEntry->id;
            $transaction->save();

            // Link cheque to transaction
            $cheque->bank_transaction_id = $transaction->id;
            $cheque->save();

            // Log activity
            activity()
                ->performedOn($cheque)
                ->causedBy(auth()->user())
                ->withProperties(['cheque_id' => $cheque->id])
                ->log('Created outgoing cheque: ' . $cheque->cheque_number);

            DB::commit();

            return redirect()->route('cheques.index')
                ->with('success', 'Outgoing cheque created successfully.');
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Error creating outgoing cheque: ' . $e->getMessage());

            return redirect()->back()
                ->withInput()
                ->with('error', 'Error creating outgoing cheque. Please try again.');
        }
    }

    /**
     * Display the specified cheque.
     *
     * @param  \App\Models\Cheque  $cheque
     * @return \Illuminate\Http\Response
     */
    public function show(Cheque $cheque)
    {
        $this->authorize('view_cheques');

        // Load related bank transaction if exists
        if ($cheque->bank_transaction_id) {
            $cheque->load('bankTransaction.journalEntry.items.chartOfAccount');
        }

        return view('banking.cheques.show', compact('cheque'));
    }

    /**
     * Show the form for editing the specified cheque.
     *
     * @param  \App\Models\Cheque  $cheque
     * @return \Illuminate\Http\Response
     */
    public function edit(Cheque $cheque)
    {
        $this->authorize('edit_cheques');

        // Only allow editing of pending or issued cheques
        if (!in_array($cheque->status, ['pending', 'issued'])) {
            return redirect()->route('cheques.show', $cheque)
                ->with('error', 'Cannot edit cheque with status: ' . $cheque->status);
        }

        $bankAccounts = BankAccount::where('is_active', true)->get();

        return view('banking.cheques.edit', compact('cheque', 'bankAccounts'));
    }

    /**
     * Update the specified cheque in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Cheque  $cheque
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Cheque $cheque)
    {
        $this->authorize('edit_cheques');

        // Only allow editing of pending or issued cheques
        if (!in_array($cheque->status, ['pending', 'issued'])) {
            return redirect()->route('cheques.show', $cheque)
                ->with('error', 'Cannot edit cheque with status: ' . $cheque->status);
        }

        $validated = $request->validate([
            'bank_account_id' => 'required|exists:bank_accounts,id',
            'cheque_number' => 'required|string|max:50',
            'cheque_date' => 'required|date',
            'amount' => 'required|numeric|min:0.01',
            'payee_name' => 'required|string|max:255',
            'payee_contact' => 'nullable|string|max:255',
            'description' => 'nullable|string',
            'due_date' => 'required|date|after_or_equal:cheque_date',
            'attachment' => 'nullable|file|mimes:pdf,jpg,jpeg,png|max:2048',
        ]);

        try {
            DB::beginTransaction();

            // Handle attachment if provided
            if ($request->hasFile('attachment')) {
                $path = $request->file('attachment')->store('cheques', 'public');
                $validated['attachment'] = $path;
            }

            $cheque->update($validated);

            // If it's an outgoing cheque with a bank transaction, update the transaction
            if ($cheque->cheque_type == 'outgoing' && $cheque->bank_transaction_id) {
                $transaction = BankTransaction::find($cheque->bank_transaction_id);

                if ($transaction) {
                    $transaction->transaction_date = $validated['cheque_date'];
                    $transaction->amount = $validated['amount'];
                    $transaction->reference_number = $validated['cheque_number'];
                    $transaction->description = 'Cheque Payment to ' . $validated['payee_name'] . ': ' . ($validated['description'] ?? '');

                    if (isset($validated['attachment'])) {
                        $transaction->attachment = $validated['attachment'];
                    }

                    $transaction->save();

                    // Update journal entry if exists
                    if ($transaction->journal_entry_id) {
                        $journalEntry = JournalEntry::find($transaction->journal_entry_id);

                        if ($journalEntry) {
                            $journalEntry->entry_date = $validated['cheque_date'];
                            $journalEntry->reference_number = 'CHQ-' . $validated['cheque_number'];
                            $journalEntry->description = 'Cheque Payment: ' . ($validated['description'] ?? '');
                            $journalEntry->save();

                            // Update journal entry items
                            foreach ($journalEntry->items as $item) {
                                $item->debit = $item->debit > 0 ? $validated['amount'] : 0;
                                $item->credit = $item->credit > 0 ? $validated['amount'] : 0;
                                $item->save();
                            }
                        }
                    }
                }
            }

            // Log activity
            activity()
                ->performedOn($cheque)
                ->causedBy(auth()->user())
                ->withProperties(['cheque_id' => $cheque->id])
                ->log('Updated cheque: ' . $cheque->cheque_number);

            DB::commit();

            return redirect()->route('cheques.show', $cheque)
                ->with('success', 'Cheque updated successfully.');
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Error updating cheque: ' . $e->getMessage());

            return redirect()->back()
                ->withInput()
                ->with('error', 'Error updating cheque. Please try again.');
        }
    }

    /**
     * Process the cheque (mark as deposited, cleared, etc.).
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Cheque  $cheque
     * @return \Illuminate\Http\Response
     */
    public function process(Request $request, Cheque $cheque)
    {
        $this->authorize('process_cheques');

        $validated = $request->validate([
            'status' => 'required|in:deposited,cleared,bounced,cancelled',
            'process_date' => 'required|date',
            'notes' => 'nullable|string',
        ]);

        try {
            DB::beginTransaction();

            $oldStatus = $cheque->status;
            $newStatus = $validated['status'];

            // Validate status transition
            $validTransitions = [
                'pending' => ['deposited', 'cleared', 'cancelled'],
                'issued' => ['cleared', 'bounced', 'cancelled'],
                'deposited' => ['cleared', 'bounced'],
                'cleared' => [],
                'bounced' => [],
                'cancelled' => [],
            ];

            if (!in_array($newStatus, $validTransitions[$oldStatus])) {
                return redirect()->back()
                    ->with('error', "Cannot change cheque status from '{$oldStatus}' to '{$newStatus}'.");
            }

            // Update cheque status
            $cheque->status = $newStatus;
            $cheque->process_date = $validated['process_date'];
            $cheque->notes = $validated['notes'] ?? $cheque->notes;
            $cheque->save();

            // Handle incoming cheque deposit
            if ($cheque->cheque_type == 'incoming' && $newStatus == 'deposited') {
                $bankAccount = $cheque->bankAccount;

                // Create bank transaction
                $transaction = new BankTransaction();
                $transaction->bank_account_id = $cheque->bank_account_id;
                $transaction->transaction_type = 'cheque_deposit';
                $transaction->transaction_date = $validated['process_date'];
                $transaction->amount = $cheque->amount;
                $transaction->reference_number = $cheque->cheque_number;
                $transaction->description = 'Cheque Deposit from ' . $cheque->payee_name;
                $transaction->cheque_id = $cheque->id;
                $transaction->save();

                // Link cheque to transaction
                $cheque->bank_transaction_id = $transaction->id;
                $cheque->save();

                // Create journal entry
                $journalEntry = new JournalEntry();
                $journalEntry->entry_date = $validated['process_date'];
                $journalEntry->reference_number = 'CHQ-DEP-' . $cheque->cheque_number;
                $journalEntry->description = 'Cheque Deposit: ' . $cheque->description;
                $journalEntry->status = 'posted';
                $journalEntry->created_by = auth()->id();
                $journalEntry->save();

                // Add journal entry items
                // Debit bank account
                $journalEntry->items()->create([
                    'chart_of_account_id' => $bankAccount->chart_of_account_id,
                    'description' => 'Cheque Deposit to ' . $bankAccount->account_name,
                    'debit' => $cheque->amount,
                    'credit' => 0,
                ]);

                // Credit accounts receivable or revenue account
                $journalEntry->items()->create([
                    'chart_of_account_id' => 40, // Assuming 40 is the ID of the Accounts Receivable account
                    'description' => 'Payment from ' . $cheque->payee_name,
                    'debit' => 0,
                    'credit' => $cheque->amount,
                ]);

                // Link transaction to journal entry
                $transaction->journal_entry_id = $journalEntry->id;
                $transaction->save();
            }

            // Handle cheque clearing
            if ($newStatus == 'cleared') {
                if ($cheque->cheque_type == 'incoming') {
                    // Update bank account balance for incoming cheque
                    $bankAccount = $cheque->bankAccount;
                    $bankAccount->current_balance += $cheque->amount;
                    $bankAccount->save();
                }

                // For outgoing cheques, the balance was already reduced when the cheque was issued
            }

            // Handle cheque bouncing
            if ($newStatus == 'bounced') {
                if ($cheque->cheque_type == 'outgoing') {
                    // Restore bank account balance for outgoing cheque
                    $bankAccount = $cheque->bankAccount;
                    $bankAccount->current_balance += $cheque->amount;
                    $bankAccount->save();

                    // Create reversal transaction
                    $transaction = new BankTransaction();
                    $transaction->bank_account_id = $cheque->bank_account_id;
                    $transaction->transaction_type = 'cheque_reversal';
                    $transaction->transaction_date = $validated['process_date'];
                    $transaction->amount = $cheque->amount;
                    $transaction->reference_number = $cheque->cheque_number;
                    $transaction->description = 'Bounced Cheque Reversal: ' . $cheque->cheque_number;
                    $transaction->cheque_id = $cheque->id;
                    $transaction->save();

                    // Create journal entry for reversal
                    $journalEntry = new JournalEntry();
                    $journalEntry->entry_date = $validated['process_date'];
                    $journalEntry->reference_number = 'CHQ-REV-' . $cheque->cheque_number;
                    $journalEntry->description = 'Bounced Cheque Reversal: ' . $cheque->description;
                    $journalEntry->status = 'posted';
                    $journalEntry->created_by = auth()->id();
                    $journalEntry->save();

                    // Add journal entry items
                    // Debit bank account (reverse the original credit)
                    $journalEntry->items()->create([
                        'chart_of_account_id' => $bankAccount->chart_of_account_id,
                        'description' => 'Bounced Cheque Reversal to ' . $bankAccount->account_name,
                        'debit' => $cheque->amount,
                        'credit' => 0,
                    ]);

                    // Credit accounts payable (reverse the original debit)
                    $journalEntry->items()->create([
                        'chart_of_account_id' => 50, // Assuming 50 is the ID of the Accounts Payable account
                        'description' => 'Bounced Cheque to ' . $cheque->payee_name,
                        'debit' => 0,
                        'credit' => $cheque->amount,
                    ]);

                    // Link transaction to journal entry
                    $transaction->journal_entry_id = $journalEntry->id;
                    $transaction->save();
                }
            }

            // Handle cheque cancellation
            if ($newStatus == 'cancelled') {
                if ($cheque->cheque_type == 'outgoing' && $oldStatus == 'issued') {
                    // Restore bank account balance for outgoing cheque
                    $bankAccount = $cheque->bankAccount;
                    $bankAccount->current_balance += $cheque->amount;
                    $bankAccount->save();

                    // Create reversal transaction
                    $transaction = new BankTransaction();
                    $transaction->bank_account_id = $cheque->bank_account_id;
                    $transaction->transaction_type = 'cheque_reversal';
                    $transaction->transaction_date = $validated['process_date'];
                    $transaction->amount = $cheque->amount;
                    $transaction->reference_number = $cheque->cheque_number;
                    $transaction->description = 'Cancelled Cheque Reversal: ' . $cheque->cheque_number;
                    $transaction->cheque_id = $cheque->id;
                    $transaction->save();

                    // Create journal entry for reversal
                    $journalEntry = new JournalEntry();
                    $journalEntry->entry_date = $validated['process_date'];
                    $journalEntry->reference_number = 'CHQ-CAN-' . $cheque->cheque_number;
                    $journalEntry->description = 'Cancelled Cheque Reversal: ' . $cheque->description;
                    $journalEntry->status = 'posted';
                    $journalEntry->created_by = auth()->id();
                    $journalEntry->save();

                    // Add journal entry items
                    // Debit bank account (reverse the original credit)
                    $journalEntry->items()->create([
                        'chart_of_account_id' => $bankAccount->chart_of_account_id,
                        'description' => 'Cancelled Cheque Reversal to ' . $bankAccount->account_name,
                        'debit' => $cheque->amount,
                        'credit' => 0,
                    ]);

                    // Credit accounts payable (reverse the original debit)
                    $journalEntry->items()->create([
                        'chart_of_account_id' => 50, // Assuming 50 is the ID of the Accounts Payable account
                        'description' => 'Cancelled Cheque to ' . $cheque->payee_name,
                        'debit' => 0,
                        'credit' => $cheque->amount,
                    ]);

                    // Link transaction to journal entry
                    $transaction->journal_entry_id = $journalEntry->id;
                    $transaction->save();
                }
            }

            // Log activity
            activity()
                ->performedOn($cheque)
                ->causedBy(auth()->user())
                ->withProperties([
                    'cheque_id' => $cheque->id,
                    'old_status' => $oldStatus,
                    'new_status' => $newStatus
                ])
                ->log("Changed cheque status from '{$oldStatus}' to '{$newStatus}'");

            DB::commit();

            return redirect()->route('cheques.show', $cheque)
                ->with('success', "Cheque status changed to '{$newStatus}' successfully.");
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Error processing cheque: ' . $e->getMessage());

            return redirect()->back()
                ->with('error', 'Error processing cheque. Please try again.');
        }
    }

    /**
     * Remove the specified cheque from storage.
     *
     * @param  \App\Models\Cheque  $cheque
     * @return \Illuminate\Http\Response
     */
    public function destroy(Cheque $cheque)
    {
        $this->authorize('delete_cheques');

        // Only allow deletion of pending or issued cheques
        if (!in_array($cheque->status, ['pending', 'issued'])) {
            return redirect()->back()
                ->with('error', 'Cannot delete cheque with status: ' . $cheque->status);
        }

        try {
            DB::beginTransaction();

            // If it's an outgoing cheque with a bank transaction, delete the transaction and restore balance
            if ($cheque->cheque_type == 'outgoing' && $cheque->bank_transaction_id) {
                $transaction = BankTransaction::find($cheque->bank_transaction_id);

                if ($transaction) {
                    // Restore bank account balance
                    $bankAccount = $cheque->bankAccount;
                    $bankAccount->current_balance += $cheque->amount;
                    $bankAccount->save();

                    // Delete journal entry if exists
                    if ($transaction->journal_entry_id) {
                        $journalEntry = JournalEntry::find($transaction->journal_entry_id);

                        if ($journalEntry) {
                            // Delete journal entry items
                            $journalEntry->items()->delete();

                            // Delete journal entry
                            $journalEntry->delete();
                        }
                    }

                    // Delete transaction
                    $transaction->delete();
                }
            }

            // Log activity before deletion
            activity()
                ->performedOn($cheque)
                ->causedBy(auth()->user())
                ->withProperties([
                    'cheque_id' => $cheque->id,
                    'cheque_number' => $cheque->cheque_number,
                    'amount' => $cheque->amount
                ])
                ->log('Deleted cheque: ' . $cheque->cheque_number);

            // Delete cheque
            $cheque->delete();

            DB::commit();

            return redirect()->route('cheques.index')
                ->with('success', 'Cheque deleted successfully.');
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Error deleting cheque: ' . $e->getMessage());

            return redirect()->back()
                ->with('error', 'Error deleting cheque. Please try again.');
        }
    }
}
