from django.shortcuts import render, get_object_or_404
import json
from decimal import Decimal
from django.conf import settings
from expenses.models import *
from store.models import *
from voucher.models import *
from masters.models import *
from stock.stock_summary import *
from store.forms import *
from masters.forms import *
from voucher.forms import *
import hashlib
import os
from datetime import datetime
from django.utils import timezone
from django.db.models import Max, F, Q
import base64
from io import BytesIO
from django.template.loader import get_template
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.templatetags.static import static
from xhtml2pdf import pisa
from num2words import num2words
from django.contrib.staticfiles import finders
from common.utils import *
from django.core.exceptions import ValidationError
from supplier.models import *
from reports.customer_ledger import outstanding_customer
from reports.party_ledger import outstanding_supplier
from reports.supply_branch_ledger import outstanding_supply_branch
from reports.finance_ledger import get_opening_finance_balance, get_finance_transactions
from reports.card_ledger import get_opening_card_balance, get_card_transactions
# ******************************************************************************************


def voucher(request):
    if 'user_id' in request.session:
        user_type = request.session.get('user_type')
        branch_id = request.session.get('branch_id')
        is_ho = request.session.get('is_ho')
        if user_type == 'stores':
            company = select_row(company_table, {'id': 1})
            expense = selectList(expense_master_table, order_by='name')
            branch = selectList(branch_table, order_by='name').exclude(id=branch_id)
            customer = selectList(customer_table, order_by='name')
            if is_ho == 1:
                bank = selectList(bank_table, order_by='name')
            else:
                bank = selectList(bank_table, {'is_default': 1}, order_by='name')
            finance = selectList(finance_table, order_by='name')
            supplier = selectList(supplier_table, order_by='name')
            return render(request, 'voucher_detail.html', {
                'company': company,
                'expense': expense,
                'branch': branch,
                'customer': customer,
                'bank': bank,
                'finance': finance,
                'supplier': supplier,
            })
        else:
            return HttpResponseRedirect("/")
    else:
        return HttpResponseRedirect("/")



def ajax_voucher_view(request):  
    from_date = request.POST.get('from_date')
    to_date = request.POST.get('to_date')
    store_id = request.POST.get('store_id')
    branch_id = request.session.get('branch_id')
    fyf_name = request.session.get('fyf')
    financial_year = calculate_financial_year(fyf_name)
    voucher_type = request.POST.get('voucher_type')
   

    role_id = request.session.get('role_id')
    has_access, error_message = check_user_access(role_id, 'voucher', "read")
    if not has_access:
        return JsonResponse({'message': 'permission', 'error_message': 'You do not have permission to view voucher details'})
    
    query = Q(status=1, branch_id=branch_id,current_fy=financial_year)
    
    if from_date and to_date:
        query &= Q(date__range=[from_date, to_date])
    
    if voucher_type:
        query &= Q(voucher_type=voucher_type)


    
    data = list(selectList(voucher_table, query, order_by='date').values())
    formatted = [
        {
            'id': index + 1,
            'action': '<button type="button" onclick="edit_data(\'{}\')" class="btn btn-outline-success btn-xs p-1"><i class="fas fa-edit"></i></button> \
                      <button type="button" onclick="delete_data(\'{}\')" class="btn btn-outline-danger btn-xs p-1"> <i class="fas fa-trash-alt"></i></button>'.format(item['id'], item['id']), 

             'type':format_badge(item['voucher_type'],mapping={
                'receipt': 'badge text-bg-success', 
                'payment': 'badge text-bg-info', 
                'debit': 'badge text-bg-info', ''
                'credit': 'badge text-bg-danger',
                'advance': 'badge text-bg-warning'
                },label_mapping={'receipt': 'Receipt', 'payment': 'Payment', 'debit': 'Debit Note', 'credit': 'Credit Note', 'advance':'Advance Amount'}),

            'supply': getItemNameById(branch_table, item['supply_branch_id'])  if item['supply_branch_id'] else '-', 
            'customer': getItemNameById(customer_table, item['customer_id'])  if item['customer_id'] else '-', 
            'supplier':getItemNameById(supplier_table, item['supplier_id'])  if item['supplier_id'] else '-', 
            'amount': format_amount(item['payable_amount'])  if item['payable_amount'] else '-', 
            'finance': getItemNameById(finance_table, item['finance_id'])  if item['finance_id'] else '-', 
            'card': getItemNameById(finance_table, item['card_id'])  if item['card_id'] else '-', 
            'receivable': format_amount(item['receivable_amount'])  if item['receivable_amount'] else '-', 
            'date': f"{format_date_month_year(item['date'])} {format_hr_m(item['time'])}" if item['date'] and item['time'] else format_date_month_year(item['date']) if item['date'] else '-',
            'remarks': item['remarks'] if item['remarks'] else '-', 
            'voucher_no': item['voucher_no'] if item['voucher_no'] else '-', 
            'mode': item['payment_mode'] if item['payment_mode'] else '-', 
            'payment': item['payment_remarks'].title() if item['payment_remarks'] else '-', 
            'status': '<span class="badge text-bg-success">Active</span>' if item['is_active'] else '<span class="badge text-bg-danger">Inactive</span>'  # Assuming is_active is a boolean field
        } 
        for index, item in enumerate(data)
    ]
    return JsonResponse({'data': formatted})




def parse_date(date_str):
    try:
        if date_str in [None, '', '0000-00-00']:
            return None
        return datetime.strptime(date_str, '%Y-%m-%d').date()
    except ValueError:  
        raise ValidationError(f"Invalid date format: {date_str}. Expected format: YYYY-MM-DD")


def add_voucher(request):
    if request.method == 'POST':
        try:
            user_id = request.session.get('user_id')           
            company_id = request.session.get('company_id')           
            branch_id = request.session.get('branch_id')           
            role_id = request.session.get('role_id')
            fyf_name = request.session.get('fyf')
            financial_year = calculate_financial_year(fyf_name)
            has_access,error_message = check_user_access(role_id, 'voucher', "create")
            date_times = timezone.localtime(timezone.now())

            if not has_access:
                return JsonResponse({'message': 'permission', 'error_message': 'You do not have permission to add voucher details'})
            
            form = voucherForm(request.POST, request.FILES)
            if form.is_valid():
                category = form.save(commit=False)
                voucher_type = request.POST.get('voucher_type')
                supply_id = request.POST.get('supply_store_id') or 0
                supplier_id = request.POST.get('supplier_id') or 0
                finance_id = request.POST.get('finance_id') or 0
                customer_id = request.POST.get('customer_id') or 0
                card_id = request.POST.get('card_id') or 0
                payable_amount = float(request.POST.get('payable_amount') or 0)
                receivable_amount = float(request.POST.get('receivable_amount')or 0)
                outstanding = float(request.POST.get('outstanding')or 0)
                mode = request.POST.get('payment_mode')
                remarks = request.POST.get('remarks')
                payment_remarks = request.POST.get('payment_remarks')        
                bank_id = request.POST.get('bank_id') or 0     

                generated_po_no = generate_serial_number(
                    model=voucher_table,
                    number_field='voucher_no',
                    financial_year_field='current_fy',
                    financial_year=financial_year,
                    branch_id=branch_id,
                    prefix="VCH"
                )
                
                if voucher_type =='payment':
                    balance_info = get_cash_and_bank_balance(branch_id=branch_id,financial_year=financial_year,bank_id=bank_id)
                    if mode == 'cash':
                        cash_balance = balance_info['cash_balance']
                        if payable_amount > cash_balance:
                            return JsonResponse({
                                'message': 'amount_check',
                                'error_message': f'Entered cash amount (₹{payable_amount:.2f}) exceeds the available store cash balance of ₹{cash_balance:.2f}.',
                                'available_balance': cash_balance
                            })
                            
                    if mode == 'bank_transfer':
                        bank_balance = balance_info['selected_bank_balance']
                        if payable_amount > bank_balance:
                            return JsonResponse({
                                'message': 'amount_check',
                                'error_message': f'Entered bank amount (₹{payable_amount:.2f}) exceeds the available store bank balance of ₹{bank_balance:.2f}.',
                                'available_balance': bank_balance
                            })


                num_series = generate_num_series(voucher_table)                
                category.company_id = company_id
                category.date = date_times.date()
                category.time=format_time(date_times)
                category.voucher_no = generated_po_no
                category.current_fy = financial_year
                category.voucher_type = voucher_type
                category.num_series= num_series
                category.voucher_no = generated_po_no
                category.branch_id = branch_id
                category.customer_id=customer_id
                category.finance_id = finance_id
                category.card_id = card_id
                category.supply_branch_id = supply_id
                category.supplier_id = supplier_id
                category.bank_id = bank_id
                category.payable_amount = payable_amount
                category.receivable_amount = receivable_amount
                category.payment_mode = mode
                category.remarks = remarks
                category.payment_remarks = payment_remarks
                category.outstanding=outstanding
                category.is_active = 1
                category.status = 1
                category.employee_id = user_id
                category.created_by = user_id
                category.updated_by = user_id
                category.created_on = format_datetime(date_times)
                category.updated_on = format_datetime(date_times)                

                category.save()
                return JsonResponse({'message': 'success'})
            else:
                errors = form.errors.as_json()
                return JsonResponse({'message': 'form_error', 'errors': errors}, status=400)

        except Exception as e:
            return JsonResponse({'message': 'exception', 'error': str(e)}, status=500)


def voucher_edit(request):
    if request.headers.get('x-requested-with') == 'XMLHttpRequest' and request.method == "POST":
        data = voucher_table.objects.filter(id=request.POST.get('id'))    
    return JsonResponse(data.values()[0])



def voucher_update(request):
    if request.method == "POST":
        try:
            voucher_id = request.POST.get('voucher_id')
            category = voucher_table.objects.get(id=voucher_id)
            user_id = request.session.get('user_id')           
            branch_id = request.session.get('branch_id')           
            role_id = request.session.get('role_id')
            fyf_name = request.session.get('fyf')
            financial_year = calculate_financial_year(fyf_name)
            has_access,error_message = check_user_access(role_id, 'voucher', "update")

            if not has_access:
                return JsonResponse({'message': 'permission', 'error_message': 'You do not have permission to update voucher details'})
            
            
            form = voucherForm(request.POST, request.FILES, instance=category)
            if form.is_valid():
                updated_category = form.save(commit=False)
                voucher_type = request.POST.get('edit_voucher_type')
                supply_id = request.POST.get('supply_store_id') or 0
                supplier_id = request.POST.get('supplier_id') or 0
                customer_id = request.POST.get('customer_id') or 0
                finance_id = request.POST.get('finance_id') or 0
                card_id = request.POST.get('card_id') or 0
                bank_id = request.POST.get('bank_id') or 0
                payable_amount = float(request.POST.get('payable_amount') or 0)
                receivable_amount = float(request.POST.get('receivable_amount') or 0)
                mode = request.POST.get('payment_mode')
                remarks = request.POST.get('remarks')
                date_times = timezone.localtime(timezone.now())
                payment_remarks = request.POST.get('payment_remarks')

                if voucher_type =='payment':
                    balance_info = get_cash_and_bank_balance(branch_id=branch_id,financial_year=financial_year,bank_id=bank_id)
                    if mode == 'cash':
                        cash_balance = balance_info['cash_balance'] + category.payable_amount or 0.00
                        if payable_amount > cash_balance:
                            return JsonResponse({
                                'message': 'amount_check',
                                'error_message': f'Entered cash amount (₹{payable_amount:.2f}) exceeds the available store cash balance of ₹{cash_balance:.2f}.',
                                'available_balance': cash_balance
                            })
                            
                    if mode == 'bank_transfer':
                        bank_balance = balance_info['selected_bank_balance'] + category.payable_amount or 0.00
                        if payable_amount > bank_balance:
                            return JsonResponse({
                                'message': 'amount_check',
                                'error_message': f'Entered bank amount (₹{payable_amount:.2f}) exceeds the available store bank balance of ₹{bank_balance:.2f}.',
                                'available_balance': bank_balance
                            })

               
                

                category.voucher_type = voucher_type
                category.finance_id = finance_id
                category.card_id = card_id
                category.bank_id = bank_id
                category.customer_id = customer_id
                category.branch_id = branch_id
                category.supply_branch_id = supply_id
                category.supplier_id = supplier_id
                category.payable_amount = payable_amount
                category.receivable_amount = receivable_amount
                category.payment_mode = mode
                category.remarks = remarks
                category.payment_remarks = payment_remarks
                category.updated_by = user_id
                category.updated_on = format_datetime(date_times)
                category.save()
                return JsonResponse({'message': 'success'})
            else:
                errors = form.errors.as_json()
                return JsonResponse({'message': 'error', 'errors': errors})
        except Exception as e:
            return JsonResponse({'message': 'exception', 'error': str(e)})



def voucher_delete(request):
    if request.method == 'POST':
        data_id = request.POST.get('id')    
        role_id = request.session.get('role_id')
        has_access, error_message= check_user_access(role_id, 'voucher', "delete")

        if not has_access:
            return JsonResponse({'message': 'permission', 'error_message': 'You do not have permission to delete voucher details'})
               
        try:
            updated = voucher_table.objects.filter(id=data_id).update(status=0)
            if updated:
                return JsonResponse({'message': 'yes'})
            else:
                return JsonResponse({'message': 'no such data'})
        except Exception as e:
            return JsonResponse({'message': 'exception', 'error': str(e)})

    return JsonResponse({'message': 'Invalid request method'})



def ajax_get_outstanding(request):
    branch_id = request.session.get('branch_id')
    fyf_name = request.session.get('fyf')
    financial_year = calculate_financial_year(fyf_name)
    
    supply_branch_id = request.POST.get('supply_branch_id')
    supplier_id = request.POST.get('supplier_id')
    customer_id = request.POST.get('customer_id')
    finance_id = request.POST.get('finance_id')
    card_id = request.POST.get('card_id')
    
    outstanding = Decimal("0.00")
    
    try:
        if supply_branch_id and str(supply_branch_id) != "0":
            res = outstanding_supply_branch(branch_id, supply_branch_id, financial_year)
            # Master balance logic from supply_branch_ledger_view
            branch_info = branch_table.objects.get(id=supply_branch_id)
            br_receivable = Decimal(str(branch_info.receivable or 0))
            br_payable = Decimal(str(branch_info.payable or 0))
            outstanding = br_payable - br_receivable
            for row in res["rows"]:
                outstanding += Decimal(row["credit"]) - Decimal(row["debit"])
                
        elif supplier_id and str(supplier_id) != "0":
            res = outstanding_supplier(branch_id, supplier_id, financial_year)
            # Logic from party_ledger_view: Outstanding = Credit - Debit
            # outstanding_supplier includes opening rows from Master Balance
            for row in res["rows"]:
                outstanding += Decimal(row["credit"]) - Decimal(row["debit"])
                
        elif customer_id and str(customer_id) != "0":
            res = outstanding_customer(branch_id, customer_id, financial_year)
            # Logic from customer_ledger_view: Outstanding = Debit - Credit
            cust = customer_table.objects.get(id=customer_id)
            c_receivable = Decimal(str(cust.receivable or 0))
            c_payable = Decimal(str(cust.payable or 0))
            outstanding = c_receivable - c_payable
            for row in res["rows"]:
                outstanding += Decimal(row["debit"]) - Decimal(row["credit"])
                
        elif finance_id and str(finance_id) != "0":
            res_open = get_opening_finance_balance(branch_id, financial_year, None, finance_id)
            outstanding = res_open["opening_balance"]
            res_trans = get_finance_transactions(branch_id, financial_year, None, None, finance_id)
            for row in res_trans["rows"]:
                outstanding += Decimal(row["debit"]) - Decimal(row["credit"])
                
        elif card_id and str(card_id) != "0":
            res_open = get_opening_card_balance(branch_id, financial_year, None, card_id)
            outstanding = res_open["opening_balance"]
            res_trans = get_card_transactions(branch_id, financial_year, None, None, card_id)
            for row in res_trans["rows"]:
                outstanding += Decimal(row["debit"]) - Decimal(row["credit"])
    except Exception as e:
        print(f"Error fetching outstanding: {e}")
            
    return JsonResponse({'outstanding': float(outstanding)})


