from django.utils import timezone
from django.utils.timezone import make_aware
from datetime import datetime
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.db import transaction
from .models import Transaction, TransactionItem, StockHistory
from categories.models import Product
from management.models import Customer, Employee
import json
import logging
from django.urls import reverse
from django.db.models import F, Sum
from django.contrib.auth.mixins import LoginRequiredMixin
from datetime import datetime
from django.views.generic import View
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.http import HttpResponse
from xhtml2pdf import pisa
from django.conf import settings
import os
from io import BytesIO
from django.utils.timezone import now
from django.template.loader import get_template
import locale
from .forms import CustomerForm
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill
from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string

logger = logging.getLogger(__name__)

@login_required
def cashier_view(request, transaction_id=None):
    # Fetch all products, customers, and employees
    products = Product.objects.all()
    customers = Customer.objects.all()
    employees = Employee.objects.all()

    # Fetch the transaction based on the provided transaction_id
    transaction = None
    if transaction_id:
        transaction = get_object_or_404(Transaction, id=transaction_id)

    return render(request, 'cashier.html', {'products': products, 'customers': customers, 'employees': employees, 'transaction': transaction})

@login_required
def cashier_update(request, transaction_id=None):
    # Fetch all products, customers, and employees
    products = Product.objects.all()
    customers = Customer.objects.all()
    employees = Employee.objects.all()

    # Fetch the transaction based on the provided transaction_id
    transaction = None
    if transaction_id:
        transaction = get_object_or_404(Transaction, id=transaction_id)

    return render(request, 'cashier-update.html', {'products': products, 'customers': customers, 'employees': employees, 'transaction': transaction})

def search_products(request):
    query = request.GET.get('query', '').strip()
    if not query:
        return JsonResponse([], safe=False)  # Kembalikan daftar kosong jika input kosong

    products = Product.objects.filter(name__icontains=query)[:10]  # Maksimal 10 hasil
    result = [
        {
            'product_number': product.product_number,
            'name': product.name,
            'stock': product.qty,
            'price': str(product.price),
        }
        for product in products
    ]
    return JsonResponse(result, safe=False)
    
@login_required
def get_product(request):
    code = request.GET.get('code', '').strip()
    if not code:
        return JsonResponse({'error': 'Product code is required'}, status=400)
    
    try:
        product = Product.objects.get(product_number=code)
        if product.qty <= 0:
            return JsonResponse({
                'error': 'Product is out of stock',
                'product_number': product.product_number,
                'name': product.name,
                'stock': 0
            }, status=400)
        
        return JsonResponse({
            'product_number': product.product_number,
            'name' : product.name,
            'price' : str(product.price),
            'stock' : product.qty,
            'status': 'available'
        })
    
    except Product.DoesNotExist:
        return JsonResponse({'error': 'Product not found'}, status=404)
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)

@login_required
def save_transaction(request):
    if request.method != 'POST':
        return JsonResponse({'error': 'Invalid method'}, status=405)

    try:
        # Log request body
        #print("Raw request body:", request.body.decode('utf-8'))

        data = json.loads(request.body)
        #print("Parsed data:", data)
        items = data.get('items', [])
        #print("Items:", items)
    
        # Validasi customer
        try:
            customer = Customer.objects.get(id=data['customer_id'])
            print("Customer found:", customer)
        except Customer.DoesNotExist:
            return JsonResponse({'error': f'Customer with id {data["customer_id"]} not found'}, status=400)
        
        # Validasi employee
        try:
            employee = Employee.objects.get(id=data['employee_id'])
        except Employee.DoesNotExist:
            return JsonResponse({'error': f'Employee with id {data["employee_id"]} not found'}, status=400)
        
        # Validasi items
        for item in items:
            required_item_fields = ['product_number', 'quantity', 'price', 'subtotal']
            for field in required_item_fields:
                if field not in item:
                    return JsonResponse({'error': f'Missing required field in item: {field}'}, status=400)
        # Validasi payment status
        payment_status = data.get('status', 'PENDING')  # Default ke 'PENDING' jika tidak diberikan
        if payment_status not in ['PENDING', 'COMPLETED']:
            return JsonResponse({'error': 'Invalid payment status'}, status=400)
        
        try:
            with transaction.atomic():
                print("Creating transaction...")
                new_transaction = Transaction.objects.create(
                    employee=employee,
                    customer=customer,
                    total_amount=data['total_amount'],
                    payment_method=data['payment_method'],
                    paid_amount=data['paid_amount'],
                    change_amount=data['change_amount'],
                    status=payment_status,
                    transaction_date=timezone.now()
                )
                print("Transaction created:", new_transaction)
                
                for item in items:
                    try:
                        product = Product.objects.get(product_number=item['product_number'])
                        print(f"Processing product: {product}")
                        
                        TransactionItem.objects.create(
                            transaction=new_transaction,
                            product=product,
                            qty=item['quantity'],
                            unit_price=item['price'],
                            subtotal=item['subtotal']
                        )
                        
                        product.qty -= item['quantity']
                        product.save()
                        print(f"Product {product} updated")
                        
                    except Product.DoesNotExist:
                        raise Exception(f'Product with number {item["product_number"]} not found')
                    
                print("All items processed successfully")
                
            return JsonResponse({
                'success': True,
                'transaction_id': new_transaction.id
            })
            
        except Exception as e:
            print("Error dalam transaksi:", str(e))
            raise
            
    except json.JSONDecodeError as e:
        print("JSON Decode Error:", str(e))
        return JsonResponse({'error': f'Invalid JSON format: {str(e)}'}, status=400)
    except Exception as e:
        print("General Error:", str(e))
        print("Error type:", type(e))
        import traceback
        print("Traceback:", traceback.format_exc())
        return JsonResponse({'error': str(e)}, status=400)

def search_employees(request):
    query = request.GET.get('query', '').strip().lower()
    if not query:
        return JsonResponse([], safe=False)

    employees = Employee.objects.filter(name__icontains=query)
    data = [{'id': emp.id, 'name': emp.name} for emp in employees]
    return JsonResponse(data, safe=False)

def search_customers(request):
    query = request.GET.get('query', '').strip().lower()
    if not query:
        return JsonResponse([], safe=False)

    customers = Customer.objects.filter(name__icontains=query)
    data = [{'id': customer.id, 'name': customer.name} for customer in customers]
    return JsonResponse(data, safe=False)

def print_invoice(request, transaction_id):
    # Ambil transaksi berdasarkan ID
    transaction = get_object_or_404(Transaction, id=transaction_id)
    transaction_items = transaction.items.all()  # Ambil semua item yang terkait dengan transaksi

    # Data untuk template
    context = {
        'transaction': transaction,
        'transaction_items': transaction_items,
    }
    return render(request, 'invoice.html', context)

def format_rupiah(amount):
        """Mengubah angka menjadi format rupiah."""
        try:
            locale.setlocale(locale.LC_ALL, 'id_ID.UTF-8')  # Set lokal ke Indonesia
        except locale.Error:
            locale.setlocale(locale.LC_ALL, '')  # Gunakan default locale jika gagal

        return locale.currency(amount, grouping=True, symbol="Rp ")

def transaction_invoice_pdf(request, transaction_id):
    """Membuat invoice transaksi dalam format PDF."""
    transaction = get_object_or_404(Transaction, id=transaction_id)
    transaction_items = transaction.items.all()

    # Format data transaksi
    formatted_transaction = {
        'id': transaction.id,
        'transaction_date': transaction.transaction_date,
        'employee': transaction.employee.name,
        'customer': transaction.customer.name,
        'customer_address': transaction.customer.address,
        'customer_phone': transaction.customer.phone_number,
        'customer_email': transaction.customer.email,
        'total_amount': format_rupiah(transaction.total_amount),
        'paid_amount': format_rupiah(transaction.paid_amount),
        'change_amount': format_rupiah(transaction.change_amount),
        'status': transaction.status,
    }

    # Format data item transaksi
    formatted_items = [
        {
            'product': item.product.name,
            'qty': item.qty,
            'unit_price': format_rupiah(item.unit_price),
            'subtotal': format_rupiah(item.subtotal),
        }
        for item in transaction_items
    ]

    context = {
        'transaction': formatted_transaction,
        'transaction_items': formatted_items,
        'now': now(),
    }

    html_string = render_to_string('invoice_pdf.html', context)

    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = f'inline; filename="invoice_{transaction.id}.pdf"'

    pisa_status = pisa.CreatePDF(
        html_string,
        dest=response,
        link_callback=lambda uri, rel: os.path.join(settings.STATIC_ROOT, uri.replace(settings.STATIC_URL, ""))
    )

    if pisa_status.err:
        return HttpResponse('Error generating PDF', status=500)

    return response

def get_add_customer_url():
    return reverse('admin:management_customer_add') 

def add_customer(request):
    if request.method == 'POST':
        form = CustomerForm(request.POST)
        if form.is_valid():
            customer = form.save()
            return JsonResponse({'success': True, 'message': 'Customer berhasil ditambahkan!', 'customer_id': customer.id})
        else:
            return JsonResponse({'success': False, 'message': form.errors.as_json()}, status=400)
    
    return JsonResponse({'success': False, 'message': 'Metode tidak diizinkan!'}, status=405)

class StockReportView(LoginRequiredMixin, View):
    template_name = 'report.html'
    pdf_template_name = 'report_pdf.html'

    def format_rupiah(self, amount):
        """Mengubah angka menjadi format rupiah."""
        locale.setlocale(locale.LC_ALL, 'id_ID.UTF-8')  # Set lokal ke Indonesia
        return locale.currency(amount, grouping=True, symbol="Rp ")
    
    def get_report_data(self, start_date, end_date):
        try:
            # Convert string dates to datetime objects and add time
            start_date = datetime.strptime(start_date, '%Y-%m-%d')
            end_date = datetime.strptime(end_date, '%Y-%m-%d')
            # Tambahkan waktu untuk start_date dan end_date
            start_date = start_date.replace(hour=0, minute=0, second=0, microsecond=0)
            end_date = end_date.replace(hour=23, minute=59, second=59, microsecond=999999)
            # Pastikan datetime tersebut aware jika menggunakan timezone
            start_date = make_aware(start_date)
            end_date = make_aware(end_date)
            
            if start_date > end_date:
                return None, "Tanggal mulai tidak boleh lebih besar dari tanggal akhir."
            
            # Query stock history
            stocks = StockHistory.objects.filter(
                Q(created_at__range=(start_date, end_date)) |
                Q(updated_at__range=(start_date, end_date))
            ).select_related(
                'product',
                'product__category',
                'supplier'
            ).order_by('date_in')

            # Query TransactionItem
            transaction_items = TransactionItem.objects.filter(
                Q(created_at__range=(start_date, end_date)) |
                Q(updated_at__range=(start_date, end_date))
            ).select_related(
                'transaction',
                'transaction__employee',
                'transaction__customer',
                'product',
                'product__category'
            ).filter(
                transaction__status='COMPLETED'
            ).order_by('updated_at')
            
            # Calculate total
            stock_total = sum(stock.subtotal for stock in stocks)
            transaction_total = sum(item.subtotal for item in transaction_items)
            balance = transaction_total - stock_total  # Saldo
            
            # Format semua angka ke dalam format rupiah sebelum dikirim ke template
            formatted_stocks = []
            for stock in stocks:
                formatted_stocks.append({
                    'product': stock.product.name,
                    'category': stock.product.category.name,
                    'supplier': stock.supplier.name,
                    'stock_in': stock.stock_in,
                    'price': self.format_rupiah(stock.product.price),
                    'subtotal': self.format_rupiah(stock.subtotal),
                    'date_in': stock.date_in,
                })

            formatted_transactions = []
            for item in transaction_items:
                formatted_transactions.append({
                    'updated_at': item.updated_at,
                    'product': item.product.name,
                    'transaction_id': item.transaction_id,
                    'category': item.product.category.name,
                    'employee': item.transaction.employee.name if item.transaction.employee else '-',
                    'customer': item.transaction.customer.name if item.transaction.customer else '-',
                    'qty': item.qty,
                    'unit_price': self.format_rupiah(item.unit_price),
                    'subtotal': self.format_rupiah(item.subtotal),
                })

            context = {
                'has_results': True,
                'stocks': formatted_stocks,
                'stock_total': self.format_rupiah(stock_total),
                'transaction_items': formatted_transactions,
                'transaction_total': self.format_rupiah(transaction_total),
                'balance': self.format_rupiah(balance),
                'start_date': start_date.strftime('%Y-%m-%d'),
                'end_date': end_date.strftime('%Y-%m-%d'),
                'now': now(),
            }
            return context, None
        except ValueError:
            return None, "Format tanggal tidak valid"

    def get(self, request):
        if request.GET.get('export') == 'excel':
            start_date = request.GET.get('start_date')
            end_date = request.GET.get('end_date')
            
            if not start_date or not end_date:
                return HttpResponse("Tanggal harus diisi", status=400)
                
            data, error = self.get_report_data(start_date, end_date)
            if error:
                return HttpResponse(error, status=400)
                
            return export_to_excel(request, data, start_date, end_date)
        
        context = {
            'has_results': False
        }
        
        start_date = request.GET.get('start_date')
        end_date = request.GET.get('end_date')
        
        if start_date and end_date:
            if start_date and end_date:
                data, error = self.get_report_data(start_date, end_date)
                if error:
                    context['error'] = error
                else:
                    context.update(data)
        
        return render(request, self.template_name, context)

def create_excel_report(data, start_date, end_date):
    """
    Membuat laporan Excel berdasarkan data yang diberikan
    """
    wb = Workbook()
    ws = wb.active
    ws.title = "Cashier Report"
    
    # Styling
    header_style = Font(bold=True)
    header_fill = PatternFill(start_color="CCE5FF", end_color="CCE5FF", fill_type="solid")
    total_fill = PatternFill(start_color="CCE5FF", end_color="CCE5FF", fill_type="solid")
    
    # Header untuk laporan barang masuk
    ws['A1'] = "LAPORAN PENGELUARAN"
    ws.merge_cells('A1:G1')
    ws['A1'].font = Font(bold=True, size=14)
    ws['A1'].alignment = Alignment(horizontal='center')
    
    # Header kolom barang masuk
    headers_expense = ['Tanggal', 'Nama Produk', 'Kategori', 'Supplier', 'Jumlah', 'Harga', 'Subtotal']
    headers_income = ['Tanggal', 'Kode Transaksi', 'Nama Produk', 'Kategori', 'Pegawai', 'Pelanggan', 'Jumlah', 'Harga', 'Subtotal']
    for col, header in enumerate(headers_expense, 1):
        cell = ws.cell(row=3, column=col)
        cell.value = header
        cell.font = header_style
        cell.fill = header_fill
    
    # Isi data barang masuk
    row = 4
    for stock in data['stocks']:
        ws.cell(row=row, column=1, value=stock['date_in'].strftime('%d/%m/%Y'))
        ws.cell(row=row, column=3, value=stock['product'])
        ws.cell(row=row, column=2, value=stock['category'])
        ws.cell(row=row, column=4, value=stock['supplier'] if stock['supplier'] else '-')
        ws.cell(row=row, column=5, value=stock['stock_in'])
        ws.cell(row=row, column=6, value=stock['price'])
        ws.cell(row=row, column=7, value=stock['subtotal'])
        row += 1
        
    # Total barang masuk
    ws.cell(row=row, column=6, value="Total").font = header_style
    ws.cell(row=row, column=7, value=data['stock_total']).font = header_style
    
    # Spasi
    row += 2
    
    # Header untuk laporan penjualan
    ws.cell(row=row, column=1, value="LAPORAN PEMASUKAN")
    ws.merge_cells(f'A{row}:I{row}')
    ws.cell(row=row, column=1).font = Font(bold=True, size=14)
    ws.cell(row=row, column=1).alignment = Alignment(horizontal='center')
    
    row += 1
    
    # Header kolom penjualan
    for col, header in enumerate(headers_income, 1):
        cell = ws.cell(row=row, column=col)
        cell.value = header
        cell.font = header_style
        cell.fill = header_fill
        
    # Isi data penjualan
    row += 1
    for item in data['transaction_items']:
        ws.cell(row=row, column=1, value=item['updated_at'].strftime('%d/%m/%Y'))
        ws.cell(row=row, column=2, value=item['transaction_id'])
        ws.cell(row=row, column=3, value=item['product'])
        ws.cell(row=row, column=4, value=item['category'])
        ws.cell(row=row, column=5, value=item['employee'] if item['employee'] else '-')
        ws.cell(row=row, column=6, value=item['customer'] if item['customer'] else '-')
        ws.cell(row=row, column=7, value=item['qty'])
        ws.cell(row=row, column=8, value=item['unit_price'])
        ws.cell(row=row, column=9, value=item['subtotal'])
        row += 1
        
    # Total penjualan dan saldo
    ws.cell(row=row, column=8, value="Total").font = header_style
    ws.cell(row=row, column=9, value=data['transaction_total']).font = header_style

    row += 2
    # Total Pengeluaran
    ws.cell(row=row, column=8, value="Total Pengeluaran").font = header_style
    ws.cell(row=row, column=8).fill = total_fill
    ws.cell(row=row, column=9, value=data['stock_total']).font = header_style
    ws.cell(row=row, column=9).fill = total_fill

    row += 1

    # Total Pemasukan
    ws.cell(row=row, column=8, value="Total Pemasukan").font = header_style
    ws.cell(row=row, column=8).fill = total_fill
    ws.cell(row=row, column=9, value=data['transaction_total']).font = header_style
    ws.cell(row=row, column=9).fill = total_fill

    row += 1

    # Saldo
    ws.cell(row=row, column=8, value="Saldo").font = header_style
    ws.cell(row=row, column=8).fill = total_fill
    ws.cell(row=row, column=9, value=data['balance']).font = header_style
    ws.cell(row=row, column=9).fill = total_fill

    
    
    # Atur lebar kolom dengan cara yang lebih aman
    from openpyxl.utils import get_column_letter
    
    for col_num in range(1, 10):  # 7 kolom (A-G)
        col_letter = get_column_letter(col_num)
        max_length = 0
        for row_num in range(1, ws.max_row + 1):
            cell = ws.cell(row=row_num, column=col_num)
            try:
                if len(str(cell.value)) > max_length:
                    max_length = len(str(cell.value))
            except:
                continue
        adjusted_width = (max_length + 2)
        ws.column_dimensions[col_letter].width = adjusted_width
    
    return wb

def export_to_excel(request, data, start_date, end_date):
    """
    Membuat response HTTP dengan file Excel
    """
    wb = create_excel_report(data, start_date, end_date)
    
    response = HttpResponse(
        content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    )
    response['Content-Disposition'] = f'attachment; filename=Cashier_Report_{start_date}_{end_date}.xlsx'
    
    wb.save(response)
    return response
    
class StockReportPDFView(StockReportView):
    pdf_template_name = 'report_pdf.html'
    
    def link_callback(self, uri, rel):
        """
        Mengonversi URL relatif menjadi path absolut agar xhtml2pdf dapat menemukan gambar.
        """
        if uri.startswith(settings.STATIC_URL):  
            return os.path.join(settings.STATIC_ROOT, uri.replace(settings.STATIC_URL, ""))
        return uri
    
    def get(self, request):
        start_date = request.GET.get('start_date')
        end_date = request.GET.get('end_date')
        
        if not (start_date and end_date):
            return HttpResponse('Tanggal harus diisi', status=400)

        data, error = self.get_report_data(start_date, end_date)
        if error:
            return HttpResponse(error, status=400)

        template = get_template(self.pdf_template_name)
        html = template.render(data)
        
        response = HttpResponse(content_type='application/pdf')
        response['Content-Disposition'] = f'inline; filename="report_{start_date}_to_{end_date}.pdf"'
        
        # Create PDF
        buffer = BytesIO()
        pisa.CreatePDF(html, dest=buffer, link_callback=self.link_callback)
        pdf = buffer.getvalue()
        buffer.close()
        
        response.write(pdf)
        return response
    
