import base64
from base64 import b64encode
import hashlib
import json
import logging
import random
import re
import string
from datetime import datetime, timedelta
from Crypto.Cipher import DES3
from Crypto.Util.Padding import pad, unpad
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import logout
from django.core.exceptions import ValidationError
from django.core.mail import send_mail
from django.core.validators import validate_email
from django.db.models import Sum, Count
from django.forms.models import model_to_dict
from django.http import JsonResponse
from django.shortcuts import render, redirect
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils import timezone
from django.utils.timezone import make_aware
from django.views.decorators.csrf import csrf_exempt
from twilio.rest import Client
import razorpay
import requests
from setting.models import SiteSetting
from urllib.parse import urlencode
from requests.auth import HTTPBasicAuth
from .models import (
    SkylinkPlan,
    OTTPlan,
    OTTSubscription,
    OTTActivationLog,
    OTTAggregator,
    VerificationCode,
    IptvRequest,
    SupportVideo,
    TVChannel
)
import urllib3
from .models import JioHotstarCode
from django.utils.timezone import now
from pprint import pprint
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
from email_template.utils import send_email_from_template
from datetime import date
from pricing_plan.models import Zone

import face_recognition
import base64
from django.shortcuts import render, redirect
from django.contrib import messages
from django.http import JsonResponse
import requests


from cashfree_pg.api_client import Cashfree
from django.shortcuts import get_object_or_404
from django.contrib.auth.decorators import login_required
from cashfree_pg.models.create_order_request import CreateOrderRequest
from django.views.decorators.csrf import csrf_exempt
import uuid
from .models import CashfreePayment
from django.http import HttpResponse, JsonResponse
from django.conf import settings
from setting.models import PaymentGatewaySettings


import base64

from Crypto.Hash import MD5




CASHFREE_ENV = (
    Cashfree.SANDBOX
    if settings.CASHFREE_ENV == "SANDBOX"
    else Cashfree.PRODUCTION
)

cashfree_client = Cashfree(
    XClientId=settings.CASHFREE_APP_ID,
    XClientSecret=settings.CASHFREE_SECRET_KEY,
    XEnvironment=CASHFREE_ENV,
)





urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Initialize Logger
logger = logging.getLogger(__name__)
# =========================
# Convert Naive Datetime to Aware
# =========================
# for record in VerificationCode.objects.all():
#     if record.timestamp.tzinfo is None:  # Check if timestamp is naive
#         record.timestamp = make_aware(record.timestamp)  # Make timezone-aware
#         record.save()

# =========================
# Get Setting Value from DB
# =========================
def get_setting_value(key):
    """Fetch and decode a setting value from SiteSetting model."""
    # try:
    #     setting = SiteSetting.objects.get(key=key)
    #     return base64.b64decode(setting.value.encode()).decode()
    # except SiteSetting.DoesNotExist:
    #     return ""

# =========================
# Initialize Razorpay Client
# =========================
# RAZORPAY_KEY_ID = get_setting_value("RAZORPAY_KEY_ID")
# RAZORPAY_SECRET = get_setting_value("RAZORPAY_SECRET")

#RAZORPAY_KEY_ID = get_setting_value("RAZORPAY_KEY_ID")
#RAZORPAY_SECRET = get_setting_value("RAZORPAY_SECRET")


# RAZORPAY_KEY_ID = settings.RAZORPAY_KEY_ID
# RAZORPAY_SECRET=  settings.RAZORPAY_KEY_SECRET

RAZORPAY_KEY_ID = "rzp_live_rS1MxKUitLtEqB"
RAZORPAY_SECRET = "bfVP9XE4hnZepjtHHHbhMfvb"


# RAZORPAY_KEY_ID = "rzp_test_qcFb099Pizad7S"
# RAZORPAY_SECRET = "z2ZULiDyrWgcu6GVYILGmVbt"

# MSG91 API Configuration
MSG91_API_URL = "https://api.msg91.com/api/sendhttp.php"
#AUTH_KEY = "127168AI5mZVVT57f23e45"  # Replace with your actual auth key
AUTH_KEY = "437052AwuTl8Nuu367ea608cP1"
SENDER_ID = "SKYLTD"
TEMPLATE_ID = "1407165460465145131"

# API_BASE_URL = "https://fiber.net.in/api/v1"
# API_AUTH_USERNAME = "freezone"
# API_AUTH_PASSWORD = "a34bea12fed31c026b9a8d4a1ac74d40e47416a6"



# NORTH_API_BASE_URL = "https://del.skyplay.app/api/v1"
# NORTH_API_AUTH_USERNAME = "skylink"
# NORTH_API_AUTH_PASSWORD = "1b7a0e2ab6ff94f13334ce2d05a1a5b9b472e72c"



if RAZORPAY_KEY_ID and RAZORPAY_SECRET:
    razorpay_client = razorpay.Client(auth=(RAZORPAY_KEY_ID, RAZORPAY_SECRET))
else:
    razorpay_client = None
    logger.error("❌ Razorpay client not initialized. API keys are missing!")



@csrf_exempt
def encrypt_watcho_payload(request):
        if request.method != "GET":
            return JsonResponse({'error': 'Only GET method allowed'}, status=405)

        try:
          #  client_phone_number = request.GET.get('phone')
            # plan_code = request.GET.get('plan_code')  # Expected from query params

            #if not client_phone_number or not plan_code:
            #   return JsonResponse({'error': 'Missing phone or plan_code'}, status=400)

            username = settings.WATCHO_API_USER_NAME
            password = settings.WATCHO_API_PASSWORD

            auth_value = f"{username}:{password}"
            encoded_auth_value = b64encode(auth_value.encode('utf-8')).decode('utf-8')

            headers = {
                "Content-Type": "application/json",
                "Authorization": f"Basic {encoded_auth_value}"
            }
            client_phone_number = 9683905441
            input_data = {
                "UserID": "188598",
                "UserType": "DL",
                "MobileNo": client_phone_number,
                "PlanId": 133733,
                "TransactionNo": generate_transaction_id(),
                "Source": "IS",
                "OTTSubscriberID": -1, 
                "SearchBy":"mobileno", 
                "SearchValue": 9683905441
            }

            json_data = json.dumps(input_data)
            enc_data = encrypt_data(json_data)
            dec_data = decrypt_data(enc_data)

            return JsonResponse({
                "headers": headers,
                "input_data": input_data,
                "encrypted_data": enc_data,
                "decrypted_check": dec_data
            })

        except Exception as e:
            return JsonResponse({'error': str(e)}, status=500)
        
@csrf_exempt

@csrf_exempt
def decrypt_watcho_payload(request):
    if request.method != "POST":
        return JsonResponse({'error': 'Only POST method allowed'}, status=405)

    try:
        # Get data from POST body (as JSON)
        body = json.loads(request.body)
        encrypted_data = body.get('encrypted_text')

        if not encrypted_data:
            return JsonResponse({'error': 'Missing "data" parameter in body'}, status=400)

        decrypted_output = decrypt_data(encrypted_data)

        return JsonResponse({
            "encrypted_input": encrypted_data,
            "decrypted_output": decrypted_output
        })

    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)

## Zoho user APIimport json

def fetch_user_by_phone(request, phone_number):
    # API URL
    
    state = request.session.get("selected_state", "TN").upper()
    if state =="TN":
        api_base_url = settings.API_BASE_URL
        api_auth_name = settings.API_AUTH_USERNAME
        api_auth_pass = settings.API_AUTH_PASSWORD
    else:
        api_base_url = settings.NORTH_API_BASE_URL
        api_auth_name = settings.NORTH_API_AUTH_USERNAME
        api_auth_pass = settings.NORTH_API_AUTH_PASSWORD

    api_url = f"{api_base_url}/get_user_by_phone/{phone_number}"

    
    # Basic Auth Credentials
    username = api_auth_name
    password = api_auth_pass
    
    # Encode credentials for Basic Auth
    auth_value = f"{username}:{password}"
    encoded_auth_value = b64encode(auth_value.encode('utf-8')).decode('utf-8')

    # Headers
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Basic {encoded_auth_value}"
    }

    try:
        # Make GET request
        response = requests.get(api_url, headers=headers, timeout=10, verify=False)
        
        # Raise an exception for HTTP errors (4xx, 5xx)
        response.raise_for_status()

        # Convert response to JSON
        return response.json() 

        # Return JsonResponse only inside Django views
       

    except requests.exceptions.SSLError:
        return JsonResponse({"error": "SSL certificate verification failed."}, status=500)
    except requests.exceptions.Timeout:
        return JsonResponse({"error": "Request timed out."}, status=500)
    except requests.exceptions.RequestException as e:
        return JsonResponse({"error": f"API request failed: {str(e)}"}, status=500)
    


# =============================
# OTT Platforms View
# =============================
def platforms_view(request):
    
    """Display active OTT platforms and Razorpay key after successful login."""
    email = request.session.get('email')
    phone_number = request.session.get('phone_number')

    if email:
        contact = email
        verification_type = 'email'       
    elif phone_number:
        contact = phone_number
        verification_type = 'phone'        
    else:
        return redirect('ott_subscription:login')
   
    selected_user = request.session.get('zoho_selected_user', None)  # Get selected user
    # Fetch Zoho user data by phone
    if phone_number:
        user_data = fetch_user_by_phone(request,phone_number)
        users = [item["User"] for data in user_data for item in data if "User" in item]
        
        user_count = len(users) 
       
        # Store in session
        request.session['zoho_all_user'] = user_data
        if user_count == 1:
            request.session['zoho_selected_user'] =user_data  # Single user, set directly
            selected_user =user_data[0]           
        elif user_count > 1:
            request.session['zoho_users'] = users  # Multiple users, let frontend decide
        #users_list = [entry["User"] for sublist in user_data for entry in sublist if "User" in entry]
        #request.session['zoho_user_data'] = user_data  # Store in session
        
 # ✅ Extract only required fields
    display_param = []
    if selected_user:
        for item in selected_user:
            if "User" in item:
                user_info = item["User"]
                billing_info = next((x.get("currentBillingCycleUsage", {}) for x in selected_user if "currentBillingCycleUsage" in x), {})
                display_param.append({
                    "name": user_info.get("name", ""),
                    "id": user_info.get("id", ""),
                    "username": user_info.get("username", ""),
                    "account_id": user_info.get("account_id", ""),
                    "phone": user_info.get("phone", ""),
                    "email": user_info.get("email", ""),
                    "status": user_info.get("status", ""),
                    "profile_image": user_info.get("profile_image", "default_profile.png"),
                    'address_city': user_info.get("address_city", ""),
                    "billingResetType": billing_info.get("billingResetType", ""),
                    "bandwidthTemplateName": billing_info.get("bandwidthTemplateName", ""),
                    
                })

    # Store display_param in session
    request.session['display_param'] = display_param
    platform_instance = OTTAggregator.objects.filter(status='active').order_by('order_id')

    tv_channels = TVChannel.objects.filter(is_active=True).order_by('order_id')
 
    tvchannel_list = [
        {
            'id': channel.id,
            'name': channel.name,
            'image': channel.image.url if channel.image else 'default_channel_image_url',
            'order_id': channel.order_id,
        }
        for channel in tv_channels
    ]

    client_id = None
    if display_param and isinstance(display_param, list) and 'id' in display_param[0]:
        client_id = display_param[0]['id']

    iptv_status = 0
    activation_code = None

    if client_id:
        iptv_entry = IptvRequest.objects.filter(client_id=client_id).first()
        if iptv_entry:
            iptv_status = 1
            activation_code = iptv_entry.activation_code


  

    return render(request, 'ott_subscription/platforms.html', {
        'contact': contact,
        'verification_type': verification_type,
        'ott_platform': platform_instance,
        'razorpay_key': RAZORPAY_KEY_ID,
        'users': users if user_count > 1 else [], 
        'user_count': user_count,
        "selected_user": selected_user,
        'user_data':user_data,
        'display_param': display_param,
        'tvchannel_list':tvchannel_list,
        'iptv_status': iptv_status,
        'activation_code': activation_code,
        'range_1_to_5': range(1, 6),  # Range for the first 5 digits
        'range_6_to_10': range(6, 11),  # Range for the last 5 digits
    })



def store_selected_user(request):
    if request.method == "POST":
        try:
            data = json.loads(request.body)
            selected_user = data.get("user")  # Could be "SKY1254" or {"id": "1245", ...}

            if not selected_user:
                return JsonResponse({"success": False, "error": "No user selected"})

            # Retrieve stored user data from the session
            zoho_all_user = request.session.get("zoho_all_user", [])

            logger.info("Stored zoho_all_user: %s", json.dumps(zoho_all_user, indent=2))
            logger.info("Selected User: %s", selected_user)

            # Search for the selected user in `zoho_all_user`
            selected_user_data = None
            for user_data in zoho_all_user:
                for item in user_data:
                    if "User" in item:
                        user = item["User"]
                        user_id = user.get("id")
                        username = user.get("username")

                        if selected_user in [user_id, username]:  # Match by ID or Username
                            selected_user_data = user_data
                            break
                if selected_user_data:
                    break

            if selected_user_data:
                request.session["zoho_selected_user"] = selected_user_data  # Store full user data
                request.session.modified = True
                return JsonResponse({"success": True, "selected_user": selected_user_data})

            return JsonResponse({"success": False, "error": "User not found in zoho_all_user"})

        except Exception as e:
            logger.error("Error in store_selected_user: %s", str(e))
            return JsonResponse({"success": False, "error": str(e)})

    return JsonResponse({"success": False})


# =============================
# Generate Random Verification Code
# =============================
def generate_verification_code():
    """Generate a random 6-digit verification code."""
    return str(random.randint(100000, 999999))


# =============================
# Send Verification Email
# =============================
def send_verification_email(email):
    """Send OTP to user's email."""
    verification_code = generate_verification_code()
    subject = "Your OTP Verification Code"
    message = f"Your verification code is {verification_code}"
    from_email = 'your-email@example.com'  # Replace with actual email

    send_mail(subject, message, from_email, [email])

    VerificationCode.objects.update_or_create(
        email=email,
        defaults={"code": verification_code, "timestamp": timezone.now()}
    )


# =============================
# Send Verification SMS
# =============================
def send_verification_sms(phone_number, name):
    """Send OTP to user's phone number using the new MSG91 API format."""
    verification_code = generate_verification_code()
    message = f"Dear {name}, Your Skylink Fibernet Verification Code is {verification_code}."
    
    # Store the verification code in the database
    VerificationCode.objects.update_or_create(
        phone_number=phone_number,
        defaults={"code": verification_code, "timestamp": timezone.now()}
    )
    # Ensure phone number has country code
    phone_number = f"91{phone_number}"
    
    # Construct the API request
    params = {
        "authkey": AUTH_KEY,
        "sender": SENDER_ID,
        "route": "4",
        "message": message,
        "mobiles": phone_number,
        "DLT_TE_ID": TEMPLATE_ID,
    }
    
    try:
        response = requests.get(MSG91_API_URL, params=urlencode(params))
        if response.status_code == 200:
            return {"success": True, "response": response.text}
        else:
            return {"success": False, "error": response.text}
    except requests.exceptions.RequestException as e:
        return {"success": False, "error": str(e)}


# =============================
# Login View
# =============================
def login_view(request):
    """Login view with OTP verification via email or phone."""
    verification_code_sent = False
    code_verified = False
    contact = ""
    verification_type = None

    if request.method == 'POST':    

        contact = request.POST.get('contact')
        entered_code = request.POST.get('verification_code')

        # ✅ Validate Email or Phone Number
        is_valid_email = False
        is_valid_phone = False

        if request.POST.get("change_contact") == "1":
            # Reset session or state for new contact entry
            request.session["verification_code_sent"] = False
            request.session["contact"] = ""
            return render(request, 'ott_subscription/login.html', {
            'verification_code_sent': verification_code_sent,
            'code_verified': code_verified,
            'contact': contact,
            'verification_type': verification_type,
            'change_contact': "0"  # Reset change_contact value
            })


        if contact:
            if '@' in contact:
                try:
                    validate_email(contact)  # Validate email format
                    is_valid_email = True
                except ValidationError:
                    messages.error(request, "Invalid email address format.")
            else:
                phone_regex = r'^\+?[1-9]\d{9,14}$'  # E.164 format for phone numbers
                if re.match(phone_regex, contact):
                    is_valid_phone = True
                else:
                    messages.error(request, "Invalid phone number format.")

        # ✅ Handle OTP Verification Code
        if entered_code:
            verification_record = None
            verification_code_sent = True

            if is_valid_email:
                verification_record = VerificationCode.objects.filter(email=contact).first()
            elif is_valid_phone:
                verification_record = VerificationCode.objects.filter(phone_number=contact).first()

            if entered_code == '994439':  # Default test code
                code_verified = True
                messages.success(request, "Verification successful with default code!")
                request.session['email' if is_valid_email else 'phone_number'] = contact
                return redirect('ott_subscription:platforms')

            if verification_record:
                if entered_code == verification_record.code:
                    code_verified = True
                    request.session['email' if is_valid_email else 'phone_number'] = contact
                    messages.success(request, "Verification successful!")
                    return redirect('ott_subscription:platforms')
                else:
                    messages.error(request, "Invalid verification code. Please try again.")
            else:
                messages.error(request, "The verification code has expired or was not found.")

        # ✅ Send Verification Code
        elif contact and (is_valid_email or is_valid_phone):
            name = 'User'
            if is_valid_email:
                send_verification_email(contact)
                verification_code_sent = True
                verification_type = 'email'
                messages.success(request, "A verification code has been sent to your email.")
            elif is_valid_phone:
                sms_send_flag = settings.SMS_SEND_FLAG
                if sms_send_flag:
                    send_verification_sms(contact, name)  # Uncomment when SMS function is ready
                verification_code_sent = True
                verification_type = 'phone'
                messages.success(request, "A verification code has been sent to your phone.")

    return render(request, 'ott_subscription/login.html', {
        'verification_code_sent': verification_code_sent,
        'code_verified': code_verified,
        'contact': contact,
        'verification_type': verification_type
    })


def login_face_view(request):
    """Login view with OTP verification via email or phone."""
    verification_code_sent = False
    code_verified = False
    contact = ""
    verification_type = None

    if request.method == 'POST':    

        contact = request.POST.get('contact')
        entered_code = request.POST.get('verification_code')

        # ✅ Validate Email or Phone Number
        is_valid_email = False
        is_valid_phone = False

        if request.POST.get("change_contact") == "1":
            # Reset session or state for new contact entry
            request.session["verification_code_sent"] = False
            request.session["contact"] = ""
            return render(request, 'ott_subscription/loginwithface.html', {
            'verification_code_sent': verification_code_sent,
            'code_verified': code_verified,
            'contact': contact,
            'verification_type': verification_type,
            'change_contact': "0"  # Reset change_contact value
            })


        if contact:
            if '@' in contact:
                try:
                    validate_email(contact)  # Validate email format
                    is_valid_email = True
                except ValidationError:
                    messages.error(request, "Invalid email address format.")
            else:
                phone_regex = r'^\+?[1-9]\d{9,14}$'  # E.164 format for phone numbers
                if re.match(phone_regex, contact):
                    is_valid_phone = True
                else:
                    messages.error(request, "Invalid phone number format.")

        # ✅ Handle OTP Verification Code
        if entered_code:
            verification_record = None
            verification_code_sent = True

            if is_valid_email:
                verification_record = VerificationCode.objects.filter(email=contact).first()
            elif is_valid_phone:
                verification_record = VerificationCode.objects.filter(phone_number=contact).first()

            if entered_code == '994439':  # Default test code
                code_verified = True
                messages.success(request, "Verification successful with default code!")
                request.session['email' if is_valid_email else 'phone_number'] = contact
                return redirect('ott_subscription:platforms')

            if verification_record:
                if entered_code == verification_record.code:
                    code_verified = True
                    request.session['email' if is_valid_email else 'phone_number'] = contact
                    messages.success(request, "Verification successful!")
                    return redirect('ott_subscription:platforms')
                else:
                    messages.error(request, "Invalid verification code. Please try again.")
            else:
                messages.error(request, "The verification code has expired or was not found.")

        # ✅ Send Verification Code
        elif contact and (is_valid_email or is_valid_phone):
            name = 'User'
            if is_valid_email:
                send_verification_email(contact)
                verification_code_sent = True
                verification_type = 'email'
                messages.success(request, "A verification code has been sent to your email.")
            elif is_valid_phone:
                sms_send_flag = settings.SMS_SEND_FLAG
                if sms_send_flag:
                    send_verification_sms(contact, name)  # Uncomment when SMS function is ready
                verification_code_sent = True
                verification_type = 'phone'
                messages.success(request, "A verification code has been sent to your phone.")

    return render(request, 'ott_subscription/loginwithface.html', {
        'verification_code_sent': verification_code_sent,
        'code_verified': code_verified,
        'contact': contact,
        'verification_type': verification_type
    })



def verify_face(request):
    if request.method == "POST":
        # Extract base64 image data
        image_data = request.POST.get("image")
        image_data = image_data.split(",")[1]  # Remove the "data:image/png;base64," part
        image = base64.b64decode(image_data)

        # Load the reference image (store this image during user registration)
        reference_image = face_recognition.load_image_file("path_to_reference_image.jpg")
        reference_encoding = face_recognition.face_encodings(reference_image)[0]

        # Load the captured image
        captured_image = face_recognition.load_image_file(image)
        captured_encoding = face_recognition.face_encodings(captured_image)[0]

        # Compare the two images
        matches = face_recognition.compare_faces([reference_encoding], captured_encoding)

        if True in matches:
            # Successful face match
            messages.success(request, "Face verification successful!")
            return JsonResponse({"success": True})
        else:
            messages.error(request, "Face verification failed.")
            return JsonResponse({"success": False})

    return JsonResponse({"success": False}, status=400)


# =============================
# Logout View
# =============================
def logout_view(request):
    """Logout the user and clear session."""
    logout(request)
    return redirect('ott_subscription:login')


## Zoho user APIimport json

def get_zoho_invoice_list(request, client_id, account_id,  paid_status):
    
    """
    Fetches unpaid invoices for a given client ID.
    """    


    state = request.session.get("selected_state", "TN").upper()
    if state =="TN":
        api_base_url = settings.API_BASE_URL
        api_auth_name = settings.API_AUTH_USERNAME
        api_auth_pass = settings.API_AUTH_PASSWORD
    else:
        api_base_url = settings.NORTH_API_BASE_URL
        api_auth_name = settings.NORTH_API_AUTH_USERNAME
        api_auth_pass = settings.NORTH_API_AUTH_PASSWORD



    #print(account_id)
    # API URL
    api_url = f"{api_base_url}/get_all_invoices_from_userid/{client_id}/{paid_status}/desc/"
    #print(api_url)
     # Payload
    payload = {      
        "accountId": account_id,       
    }

    
    # Basic Auth Credentials
    username = api_auth_name
    password = api_auth_pass

    # Encode credentials for Basic Auth
    auth_value = f"{username}:{password}"
    encoded_auth_value = b64encode(auth_value.encode('utf-8')).decode('utf-8')

    # Headers
    headers = {       
        "Authorization": f"Basic {encoded_auth_value}"
    }
    try:
        # Make GET request with SSL warning disabled
        response = requests.post(api_url, headers=headers, json=payload, timeout=10, verify=False)
       
        # Raise an exception for HTTP errors (4xx, 5xx)
        response.raise_for_status()
        
        response_data = response.json()    

        # Extract invoice IDs from the first list
         # Debugging: Print response data        

        # Ensure response is a list before accessing index 0
        if isinstance(response_data, list) and response_data:
            return response_data[0]  # Return first item safely
        else:
            return JsonResponse({"error": "Unexpected response format or empty data."}, status=500)


    except requests.exceptions.SSLError:
        return JsonResponse({"error": "SSL certificate verification failed."}, status=500)
    except requests.exceptions.Timeout:
        return JsonResponse({"error": "Request timed out."}, status=500)
    except requests.exceptions.RequestException as e:
        return JsonResponse({"error": f"API request failed: {str(e)}"}, status=500)
        return JsonResponse({"error": f"API request failed: {str(e)}"}, status=500)
    
# Zoho invoice details API import json
def get_zoho_invoice_details(request, invoice_id):
    
    """
    Fetches unpaid invoices for a given client ID.
    """
    state = request.session.get("selected_state", "TN").upper()
    if state =="TN":
        api_base_url = settings.API_BASE_URL
        api_auth_name = settings.API_AUTH_USERNAME
        api_auth_pass = settings.API_AUTH_PASSWORD
    else:
        api_base_url = settings.NORTH_API_BASE_URL
        api_auth_name = settings.NORTH_API_AUTH_USERNAME
        api_auth_pass = settings.NORTH_API_AUTH_PASSWORD

    # API URL
    api_url = f"{api_base_url}/get_invoice_details/{invoice_id}"
   

    # Basic Auth Credentials
    username = api_auth_name
    password = api_auth_pass
    # Encode credentials for Basic Auth
    auth_value = f"{username}:{password}"
    encoded_auth_value = b64encode(auth_value.encode('utf-8')).decode('utf-8')

    # Headers
    headers = {       
        "Authorization": f"Basic {encoded_auth_value}"
    }
    try:
        # Make GET request with SSL warning disabled
        response = requests.get(api_url, headers=headers, timeout=10, verify=False)
       
        # Raise an exception for HTTP errors (4xx, 5xx)
        response.raise_for_status()
        
        response_data = response.json()   
   
        # Extract invoice IDs from the first list
        return response_data if response_data else []

    except requests.exceptions.SSLError:
        return JsonResponse({"error": "SSL certificate verification failed."}, status=500)
    except requests.exceptions.Timeout:
        return JsonResponse({"error": "Request timed out."}, status=500)
    except requests.exceptions.RequestException as e:
        return JsonResponse({"error": f"API request failed: {str(e)}"}, status=500)
        return JsonResponse({"error": f"API request failed: {str(e)}"}, status=500)


def get_skylink_plans(request):
    plans_data = []
    amount = 0
    platform_to_check = int(request.GET.get('platform_id', 0) or 0)
    sky_plan_id = int(request.GET.get('sky_plan_id', 0) or 1)
    client_id = int(request.GET.get('client_id', 0) or 0)
    plan_type = int(request.GET.get('plan_type', 0) or 0)
    display_param = request.session.get('display_param', [])
    if not display_param:
        return redirect('ott_subscription:login')   
  

      # Find the client phone number from display_param based on client_id
    account_id = display_param[0].get('account_id', '')
    brand_width = display_param[0].get('bandwidthTemplateName', '')
    customerStatus = display_param[0].get('status', '')

    if customerStatus.lower() != 'active':
        message = """Dear customer, your account is currently inactive.<br>
To restore your OTT access, please contact our support team  or <a href='https://www.skylink.net.in/inidvidual-broadband/contact-us/' target='_blank'>click here</a> to resolve the issue."""


        html_content = render_to_string('ott_subscription/ott_plans.html', {
            'plans': plans_data,
            'client_id': client_id,
            'platform_id': platform_to_check,
            'un_paid_invoice_flag': 1,
            'message': message
        })
        return JsonResponse({'html': html_content})


    
    un_paid_invoice_list = get_zoho_invoice_list(request, client_id, account_id, 'unpaid')
    if un_paid_invoice_list  and plan_type == 0:
        message = f""" Dear customer, Pending invoice: {', '.join(un_paid_invoice_list)}.<br> Please <a href='https://www.skylinknet.in/customer_portal/account/sn' target='_blank'>click here</a> to complete the payment and ensure uninterrupted OTT access."""
        html_content = render_to_string('ott_subscription/ott_plans.html', {
            'plans': plans_data,
            'client_id': client_id,
            'platform_id': platform_to_check,
            'un_paid_invoice_flag': 1,
            'message': message
        })
        return JsonResponse({'html': html_content})

    paid_plain_invoices = get_zoho_invoice_list(request, client_id, account_id, 'paid')
    if not paid_plain_invoices and plan_type == 0:
        message = "No paid invoices found."
        html_content = render_to_string('ott_subscription/ott_plans.html', {
            'plans': plans_data,
            'client_id': client_id,
            'platform_id': platform_to_check,
            'un_paid_invoice_flag': 1,
            'message': message
        })
        return JsonResponse({'html': html_content})
    hotstar_enabled = OTTAggregator.objects.filter(status='active', code='hotstar').exists()
    # Prepare response data
    plans_data = []


     # Fetch all aggregators once
    aggregators_dict = {
        agg.id: {
            'name': agg.name,
            'code': agg.code,
            'play_store_url': agg.play_store_url or '',
            'app_store_url': agg.app_store_url or '',
            'play_store_icon': agg.play_store_icon.url if agg.play_store_icon else '',
            'app_store_icon': agg.app_store_icon.url if agg.app_store_icon else '',
        }
        for agg in OTTAggregator.objects.all()
    }



    # Process free plans
    flexible_free_plans = []
    non_flexible_free_plans = []
    plans_by_group = {}
    first_invoice_id = 0 
    billing_cycle = 1
    # only for free plan 
    if plan_type == 0:
        first_invoice_id = paid_plain_invoices[0]
        invoice_details = get_zoho_invoice_details(request,first_invoice_id)
        if invoice_details and isinstance(invoice_details, list) and len(invoice_details) > 0:
            first_invoice = invoice_details[0]
            details_of_invoice = first_invoice.get("detailsOfInvoice", [])     

            billing_from_date = first_invoice.get("billingPeriodFrom", [])
            billing_to_date = first_invoice.get("billingPeriodTo", [])   

            # Convert strings to datetime objects
            billing_from_date = datetime.strptime(billing_from_date, "%Y-%m-%d")
            billing_to_date = datetime.strptime(billing_to_date, "%Y-%m-%d")
            # Calculate the difference in days
            date_difference = (billing_to_date - billing_from_date).days

            # Calculate the billing cycle in months
            billing_cycle = (billing_to_date.year - billing_from_date.year) * 12 + (billing_to_date.month - billing_from_date.month)
            
            # Normalize billing cycle
            if billing_cycle == 2:
                billing_cycle = 1
            elif billing_cycle == 5:
                billing_cycle = 6
            elif billing_cycle == 7:
                billing_cycle = 6
            elif billing_cycle == 8:
                billing_cycle = 6
            elif billing_cycle == 13:
                billing_cycle = 12
            elif billing_cycle == 14:
                billing_cycle = 12
            elif billing_cycle == 15:
                billing_cycle = 12

            
            # Store in session 
            request.session['billing_cycle'] = billing_cycle
            request.session.modified = True 
            if isinstance(details_of_invoice, list) and len(details_of_invoice) > 1:
                amount = details_of_invoice[1].get("amount", "0.00")
              
            else:
                amount = 0
                message = "Amount not found in details of invoice."
                html_content = render_to_string('ott_subscription/ott_plans.html', {
                    'plans': plans_data,
                    'client_id': client_id,
                    'platform_id': platform_to_check,
                    'un_paid_invoice_flag': 1,
                    'message': message
                })
                return JsonResponse({'html': html_content})
        else:
            amount = 0
            message = "No invoice details available."
            html_content = render_to_string('ott_subscription/ott_plans.html', {
                'plans': plans_data,
                'client_id': client_id,
                'platform_id': platform_to_check,
                'un_paid_invoice_flag': 1,
                'message': message
            })
            return JsonResponse({'html': html_content})

        #skylink_plan = SkylinkPlan.objects.filter(amount=amount).first()
        # Retrieve the corresponding Zone object
        try:
            zone = Zone.objects.get(zone_name=account_id)
            client_service_type = zone.service_type
        except Zone.DoesNotExist:
            client_service_type = None  # Handle the case where the Zone doesn't exist

        
        brand_width = brand_width.strip()
        normalized = brand_width.replace("mbps", "Mbps").replace("MBPS", "Mbps")

        cleaned_bandwidth = normalized.split("Mbps")[0].strip() + " Mbps"


        
        # Base queryset
        # skylink_plan_qs = SkylinkPlan.objects.filter(
        #     amount=amount,
        #     bandwidth_verified_flag=True,
        #     bandwidth=cleaned_bandwidth
        # )                                                                                                  

        # # Apply server_type filter only if client_service_type is not None
        # if client_service_type and client_service_type.lower() != 'skyplay':
        #     skylink_plan_qs = skylink_plan_qs.filter(server_type=client_service_type)

        # # Retrieve the first matching plan
        # skylink_plan = skylink_plan_qs.first()


        
        # Convert amount to Decimal
        base_amount = Decimal(amount)
        amounts_to_check = [base_amount]  # always include original amount

        # Add discounted amount if billing_cycle is 6 or 12
        if billing_cycle == 6:
            discounted_amount = (base_amount / Decimal("0.925")).quantize(Decimal("1"), rounding=ROUND_HALF_UP)
            amounts_to_check.append(discounted_amount)
            amounts_to_check.append(discounted_amount+1)
            amounts_to_check.append(discounted_amount-1)
    
        elif billing_cycle == 12:
            discounted_amount = (base_amount / Decimal("0.85")).quantize(Decimal("1"), rounding=ROUND_HALF_UP)
            amounts_to_check.append(discounted_amount)
            amounts_to_check.append(discounted_amount+1)
            amounts_to_check.append(discounted_amount-1)
        
        
       
        

        # Base queryset
        skylink_plan_qs = SkylinkPlan.objects.filter(
            amount__in=amounts_to_check,  # check both original and discounted
            bandwidth_verified_flag=True,
            status='active',
            bandwidth=cleaned_bandwidth
        )

        # Apply server_type filter only if client_service_type is not None
        if client_service_type and client_service_type.lower() != 'skyplay':
            skylink_plan_qs = skylink_plan_qs.filter(server_type=client_service_type)

        # Retrieve the first matching plan
        skylink_plan = skylink_plan_qs.first()

        request.session['billing_cycle'] = billing_cycle
        request.session['invoice_id'] = first_invoice_id
        request.session['billing_from_date'] =billing_from_date.isoformat()
        request.session['billing_to_date'] =billing_to_date.isoformat()
        request.session['invoice_amount'] = amount

        # If no plan is found and client_service_type was specified, try without server_type filter
        # if not skylink_plan:
        #     skylink_plan = SkylinkPlan.objects.filter(
        #         amount=amount,         
        #     ).first()
            

        request.session['skylink_plan_amount'] = amount  # Store amount in session
        if not skylink_plan:
            message = "No Skylink Plan found with the specified amount."
            html_content = render_to_string('ott_subscription/ott_plans.html', {
                'plans': plans_data,
                'client_id': client_id,
                'platform_id': platform_to_check,
                'un_paid_invoice_flag': 1,
                'message': message
            })
            return JsonResponse({'html': html_content})

        # Retrieve all OTT plans associated with the SkylinkPlan (free plans)
        free_ott_plans = skylink_plan.ott_plans.all()

        # First, separate flexible and non-flexible free plans
        for ott_plan in free_ott_plans:
            if ott_plan.flexible_plan_flag:
                flexible_free_plans.append(ott_plan)
            else:
                non_flexible_free_plans.append(ott_plan)

        # Check if any flexible plan is expired (we assume checking the first one is enough)
        flexible_plan_status_flag = 0  # default: not expired
        flexible_plan_status_flag = 0  # Default: not expired

        if flexible_free_plans:
            # Loop through all flexible plans
            for any_plan in flexible_free_plans:
                is_expired, expiration_date = is_plan_expired(client_id, any_plan.id)
                
                # If any plan is expired, set the flag to 1 and break the loop
                if is_expired:
                    flexible_plan_status_flag = 1
                    break  # Exit the loop once we find the first expired plan

        # Now build `plans_data`
        plans_data = []

        # Non-flexible plans: regular individual check
        for ott_plan in non_flexible_free_plans:


            platform_id = ott_plan.platform_id.id  # or aggregator_id

            # Get URLs
            platform_data = aggregators_dict.get(platform_id, {})
            play_store_url = platform_data.get('play_store_url', '')
            app_store_url = platform_data.get('app_store_url', '')

            play_store_icon = platform_data.get('play_store_icon', '')
            app_store_icon = platform_data.get('app_store_icon', '')
            aggregator_name = platform_data.get('name', '')
    

            associated_otts = ott_plan.otts.all().order_by('order_id')
            otts_data = [
                {
                    'id': ott.id,
                    'name': ott.name,
                    'code': ott.code,
                    'is_active': ott.is_active,
                    'image': ott.image.url if ott.image else 'default_image_url',
                }
                for ott in associated_otts
            ]

            is_expired, expiration_date = is_plan_expired(client_id, ott_plan.id)
            plan_status = 1 if is_expired else 0

            plans_data.append({
                'id': ott_plan.id,
                'code': ott_plan.code,
                'name': ott_plan.name,
                'price': ott_plan.price,
                'subscription_tiers': 'free',
                'otts': otts_data,
                'status_flag': plan_status,
                'expiration_date': expiration_date if expiration_date else 'N/A',
                'hotstar_enabled': hotstar_enabled,
                'display_param': display_param,
                'platform_id': ott_plan.platform_id.id,
                'flexible_plan_flag': ott_plan.flexible_plan_flag,
                'play_store_url': play_store_url,
                'app_store_url': app_store_url,
                'play_store_icon':play_store_icon,
                'app_store_icon':app_store_icon,
                'aggregator_name':aggregator_name
            })

        # Flexible plans: use same status for all
        for index, ott_plan in enumerate(flexible_free_plans, start=1):

            platform_id = ott_plan.platform_id.id  # or aggregator_id

            # Get URLs
            platform_data = aggregators_dict.get(platform_id, {})
            play_store_url = platform_data.get('play_store_url', '')
            app_store_url = platform_data.get('app_store_url', '')

            play_store_icon = platform_data.get('play_store_icon', '')
            app_store_icon = platform_data.get('app_store_icon', '')
            aggregator_name = platform_data.get('name', '')
        
            associated_otts = ott_plan.otts.all().order_by('order_id')
            otts_data = [
                {
                    'id': ott.id,
                    'name': ott.name,
                    'code': ott.code,
                    'is_active': ott.is_active,
                    'order_id': ott.order_id,
                    'image': ott.image.url if ott.image else 'default_image_url',
                }
                for ott in associated_otts
            ]

            _, expiration_date = is_plan_expired(client_id, ott_plan.id)

            plans_data.append({
                'id': ott_plan.id,
                'code': ott_plan.code,
                'name': ott_plan.name,
                'price': ott_plan.price,
                'subscription_tiers': 'free',
                'otts': otts_data,
                'status_flag': flexible_plan_status_flag,
                'expiration_date': expiration_date if expiration_date else 'N/A',
                'hotstar_enabled': hotstar_enabled,
                'display_param': display_param,
                'platform_id': ott_plan.platform_id.id,
                'flexible_plan_flag': ott_plan.flexible_plan_flag,
                'order_id': index,
                'play_store_url': play_store_url,
                'app_store_url': app_store_url,
                'play_store_icon':play_store_icon,
                'app_store_icon':app_store_icon,
                'aggregator_name':aggregator_name
            })
        #if end

    plans_by_group = {}
    if plan_type == 1:
      

        if platform_to_check == 0:
            aggregators = OTTAggregator.objects.filter(status='active').exclude(code='hotstar')
            aggregator_ids = aggregators.values_list('id', flat=True)
        else:
            aggregator_ids = [platform_to_check]

        for aggregator_id in aggregator_ids:


            platform_id = aggregator_id  # or aggregator_id

            # Get URLs
            platform_data = aggregators_dict.get(platform_id, {})
            play_store_url = platform_data.get('play_store_url', '')
            app_store_url = platform_data.get('app_store_url', '')

            play_store_icon = platform_data.get('play_store_icon', '')
            app_store_icon = platform_data.get('app_store_icon', '')
            aggregator_name = platform_data.get('name', '')

            # Fetch OTT plans related to the aggregator, ordered by plan_group.order_id
            ott_plans = OTTPlan.objects.filter(platform_id=aggregator_id, plan_group__isnull=False).select_related('plan_group').order_by('plan_group__order_id')

            for ott_plan in ott_plans:
                group = ott_plan.plan_group
                if not group:
                    continue

                # Unique key per group
                group_key = f"{aggregator_id}_{group.id}" if platform_to_check == 0 else str(group.id)

                if group_key not in plans_by_group:
                    associated_otts = ott_plan.otts.all().order_by('order_id')

                    plans_by_group[group_key] = {
                        'group_name': group.name,
                        'otts': [
                            {
                                'id': ott.id,
                                'name': ott.name,
                                'code': ott.code,
                                'is_active': ott.is_active,
                                'image': ott.image.url if ott.image else 'default_image_url',
                            } for ott in associated_otts
                        ],
                        'display_param': display_param,
                        'platform_id': aggregator_id,
                        'subscription_tiers': 'paid',
                        'hotstar_enabled': False,
                        'flexible_plan_flag': ott_plan.flexible_plan_flag,
                        'plans': []
                    }

                # Plan info
                is_expired, expiration_date = is_plan_expired(client_id, ott_plan.id)
                plans_by_group[group_key]['plans'].append({
                    'id': ott_plan.id,
                    'code': ott_plan.code,
                    'name': ott_plan.name,
                    'price': ott_plan.price,
                    'validity_months': ott_plan.validity_months,
                    'status_flag': 1 if is_expired else 0,
                    'expiration_date': expiration_date or 'N/A',
                    'play_store_url': play_store_url,
                    'app_store_url': app_store_url,
                    'play_store_icon':play_store_icon,
                    'app_store_icon':app_store_icon,
                    'aggregator_name':aggregator_name
                })




                
       # pprint(plans_by_group)
    template_name = 'ott_subscription/jiostart.html' if platform_to_check == 4 else 'ott_subscription/ott_plans.html'
    html_content = render_to_string(template_name, {
        'plans': plans_data,
        'client_id': client_id,
        'platform_id': platform_to_check,
        'plan_type':plan_type,
        'un_paid_invoice_flag': 0,
        'flexible_free_plans_count': len(flexible_free_plans),  # Pass count to template
        'plans_by_group': plans_by_group,
    })

    return JsonResponse({'html': html_content})
    
    
def log_ott_activation(
    request,
    client_id,
    platform_instance,
    plan_id,
    status,
    message,
    input_data,
    response_status,
    output_data,
    endpoint="",
    subscription_tiers='free',
    razorpay_details=None,
    payment_gateway=None,
    payment_data=None
):
    """
    Common function to log OTT activation (Free / Paid)
    Supports Razorpay & Cashfree
    """

    # ✅ Session safety (NO redirect inside logger)
    display_param = request.session.get('display_param', [])
    phone = display_param[0].get('phone') if display_param else None

    # 🔹 Billing info from session
    billing_cycle = int(request.session.get("billing_cycle", 1))
    invoice_id = request.session.get("invoice_id")
    invoice_amount = request.session.get("invoice_amount")

    billing_from_date = None
    billing_to_date = None

    # 🔹 Convert billing dates safely
    try:
        if request.session.get("billing_from_date"):
            billing_from_date = datetime.fromisoformat(
                request.session["billing_from_date"]
            ).date()

        if request.session.get("billing_to_date"):
            billing_to_date = datetime.fromisoformat(
                request.session["billing_to_date"]
            ).date()
    except ValueError:
        pass

    # 🔹 Fallback from Zoho (if session missing)
    try:
        current_usage = request.session["zoho_selected_user"][0][1]["currentBillingCycleUsage"]

        if billing_from_date is None:
            billing_from_date = datetime.fromisoformat(
                current_usage["billingStartDate"]
            ).date()

        if billing_to_date is None:
            billing_to_date = datetime.fromisoformat(
                current_usage["billingEndDate"]
            ).date()

    except (KeyError, IndexError, ValueError):
        pass

    # 🔹 Invoice amount
    if invoice_amount is not None:
        try:
            invoice_amount = Decimal(str(invoice_amount))
        except (InvalidOperation, ValueError):
            invoice_amount = None

    # ✅ Base log payload
    log_data = {
        "client_id": client_id,
        "platform_id": platform_instance,
        "plan_id": plan_id,
        "status": status,
        "message": message,
        "input": input_data,          # ✅ dict, not json.dumps
        "response_status": response_status,
        "output": output_data,        # ✅ dict, not json.dumps
        "endpoint": endpoint,
        "subscription_tiers": subscription_tiers,
        "phone_number": phone,
        "billing_cycle": billing_cycle,
        "invoice_id": invoice_id,
        "billing_from_date": billing_from_date,
        "billing_to_date": billing_to_date,
        "invoice_amount": invoice_amount,
        "payment_gateway": payment_gateway,
        "payment_raw_data": payment_data,
    }

    # 🔹 Razorpay (legacy support)
    if subscription_tiers == "paid" and payment_gateway == "razorpay" and razorpay_details:
        log_data.update({
            "razorpay_order_id": razorpay_details.get("razorpay_order_id"),
            "razorpay_payment_id": razorpay_details.get("razorpay_payment_id"),
            "razorpay_signature": razorpay_details.get("razorpay_signature"),
            "payment_amount": razorpay_details.get("payment_amount"),
            "payment_currency": razorpay_details.get("payment_currency"),
            "payment_verified": True,
        })

    # 🔹 Cashfree
    if subscription_tiers == "paid" and payment_gateway == "cashfree" and payment_data:
        log_data.update({
            "cashfree_order_id": payment_data.get("order_id"),
            "cashfree_payment_id": payment_data.get("cf_payment_id"),
            "cashfree_payment_session_id": payment_data.get("payment_session_id"),
            "cashfree_raw_response": payment_data,
            "payment_amount": payment_data.get("order_amount"),
            "payment_currency": payment_data.get("order_currency", "INR"),
            "payment_verified": True,
        })

    # ✅ Create log
    OTTActivationLog.objects.create(**log_data)

def is_plan_expired(client_id, plan_id):
    activation_log = OTTActivationLog.objects.filter(client_id=client_id, plan_id=plan_id).order_by('-id').first()

    if not activation_log:
        return False, None  # No activation log found

    expiration_date = activation_log.billing_to_date

    if expiration_date:
        formatted_expiration_date = expiration_date.strftime('%d-%m-%Y')  # Format as dd-mm-yyyy
        current_date = timezone.now().date()  # Get current date

        # Compare directly since expiration_date is already a date object
        if expiration_date <= current_date:
            return False, formatted_expiration_date  # Plan expired
        else:
            return True, formatted_expiration_date  # Plan is still active

    return False, None  # No expiration date available


@csrf_exempt
def ott_activation(request):
    try:
        # Get the data from the request body
        data = json.loads(request.body)
        client_id = data.get('client_id')
        platform_id = data.get('platform_id')
        plan_id = data.get('plan_id')
        subscription_tiers = data.get('subscription_tiers', 'free')

        platform_instance = OTTAggregator.objects.get(id=platform_id)
        platform_code = platform_instance.code
     
      
        # Initialize variables to store API response data
        platform_data = None
        response_status = None
        output = None
        endpoint = ""
        input_data = data  # The request data to be logged as 'input'

        # Process the subscription (add your subscription logic)
        subscription = OTTSubscription.objects.create(
            client_id=client_id,
            platform_id=platform_instance,
            plan_id=plan_id,
        )

        logger.info(f"Object retrieved: {platform_code}")
        
        print(platform_code)
        # Fetch platform-specific data based on platform code
        print("call1")
        print(platform_code)
        if platform_code == "watcho":
            print("call1")
            platform_data = fetch_watcho_data(request)
        elif platform_code == 'play_box':
            platform_data = fetch_playbox_data(request)
        elif platform_code == 'hotstar':
            platform_data = fetch_hotstar_data(request)
        elif platform_code == 'ottplay':
            platform_data = fetch_ottplay_data(request)
        elif platform_code == 'tatabinge':
            platform_data = fetch_tatabinge_data(request)
        else:
            platform_data = None
        print(platform_data)
        messages = 'The activation has been completed successfully.'
        if platform_data:
            response_content = platform_data.content  # This is in bytes format
            response_json = json.loads(response_content.decode('utf-8'))  # Convert bytes to JSON
            response_status = response_json['response_status']        
            messages = response_json['message']          
            output = response_json if response_status == 200 else response_json['output']
            endpoint = response_json['endpoint']
            input_data = response_json['input']
        else:
            response_status = 400  # Bad request if platform data is None
            output = "Invalid platform ID or failed to fetch platform data."
            endpoint = ""

        # Handle logging
        if subscription_tiers == 'paid':
              
            razorpay_details = {
                'razorpay_order_id': data.get('razorpay_order_id'),
                'razorpay_payment_id': data.get('razorpay_payment_id'),
                'razorpay_signature': data.get('razorpay_signature'),
                'payment_amount': float(data.get('payment_amount', 0)) / 100,  # Convert paise to INR
                'payment_currency': data.get('payment_currency', 'INR')
            }
            log_ott_activation(request, client_id, platform_instance, plan_id, 'Success', messages, input_data, response_status, output, endpoint, subscription_tiers, razorpay_details ,    payment_gateway="razorpay")
        else:
            log_ott_activation(request, client_id, platform_instance, plan_id, 'Success', messages, input_data, response_status, output, endpoint, subscription_tiers)

        return JsonResponse({"success": True, "message": messages})

    except Exception as e:
        # Log failure if an exception occurs
        log_ott_activation(request, client_id, platform_instance, plan_id, 'Failure', str(e), str(e), 500, str(e), "", subscription_tiers)

        return JsonResponse({"success": False, "message": str(e)})
    
    
def generate_transaction_id():
    random_part = ''.join(random.choices(string.digits, k=5))
    return f"TRAN{random_part}"

#Live
PASS_PHRASE = "W8n$e2s5v8y/B?E(H+Kb"

# Generate 16-byte key from MD5 hash (same as PHP)
def get_key(pass_phrase):
    return hashlib.md5(pass_phrase.encode()).digest()

#Live
def encrypt_data(plain_text):
    key = get_key(PASS_PHRASE)
    cipher = DES3.new(key, DES3.MODE_ECB)  # ECB mode (same as PHP)

    padded_data = pad(plain_text.encode(), 8)  # PKCS7 padding to 8 bytes
    encrypted_bytes = cipher.encrypt(padded_data)

    # Base64 encode and make it URL-safe
    encrypted_text = base64.b64encode(encrypted_bytes).decode()
    return encrypted_text.replace("+", "-").replace("/", "_")

# Decrypt function (Equivalent to PHP openssl_decrypt)
def decrypt_data(encrypted_text):
    key = get_key(PASS_PHRASE)
    cipher = DES3.new(key, DES3.MODE_ECB)  # ECB mode (same as PHP)

    # Handle URL-safe Base64 decoding
    encrypted_bytes = base64.b64decode(encrypted_text.replace("-", "+").replace("_", "/"))
    
    decrypted = cipher.decrypt(encrypted_bytes)
    return unpad(decrypted, 8).decode()


# UAT Code 
#PASS_PHRASE = "p2s5v8y/B?E(H+Kb"



# def encrypt_data(message: str) -> str:
#     # Step 1: Hash the passphrase using MD5 (128-bit key)
#     md5 = MD5.new()
#     md5.update(PASS_PHRASE.encode("utf-8"))
#     tdes_key = md5.digest()

#     # Step 2 & 3: Create TripleDES cipher (ECB + PKCS7)
#     cipher = DES3.new(tdes_key, DES3.MODE_ECB)

#     # Step 4: Convert message to bytes
#     data_to_encrypt = message.encode("utf-8")

#     # Step 5: Encrypt
#     encrypted_bytes = cipher.encrypt(pad(data_to_encrypt, DES3.block_size))

#     # Step 6: Return Base64 encoded string
#     return base64.b64encode(encrypted_bytes).decode("utf-8")

# def decrypt_data(message: str) -> str:
#     # Step 1: Create MD5 hash of passphrase (128-bit key)
#     md5 = MD5.new()
#     md5.update(PASS_PHRASE.encode("utf-8"))
#     tdes_key = md5.digest()

#     # Step 2: Fix Base64 characters (same as C#)
#     message = message.replace("_", "/").replace("-", "+")

#     # Step 3: Base64 decode
#     encrypted_data = base64.b64decode(message)

#     # Step 4: Create TripleDES cipher (ECB mode)
#     cipher = DES3.new(tdes_key, DES3.MODE_ECB)

#     # Step 5: Decrypt and remove PKCS7 padding
#     decrypted_data = cipher.decrypt(encrypted_data)
#     decrypted_data = unpad(decrypted_data, DES3.block_size)

#     # Step 6: Return UTF-8 string
#     return decrypted_data.decode("utf-8")


def fetch_watcho_data(request):
    print("call2")
    api_url = settings.WATCHO_API_URL
    username = settings.WATCHO_API_USER_NAME
    password = settings.WATCHO_API_PASSWORD
    print(api_url)
    print(username)
    print(password)
    
    data = json.loads(request.body)   
    print("data")
    print(data)
    client_id = data.get('client_id')
    platform_id = data.get('platform_id')
    plan_id = data.get('plan_id')
    plan = OTTPlan.objects.get(id=plan_id)
    code = plan.code
    print("client_id")
    print(client_id)
    print(platform_id)
    print(plan_id)
    print(code)
    # Retrieve display_param from session
    manual_flag = str(data.get("manual_flag", "0")) == "1"
    print(manual_flag)
    if manual_flag == 1:
        client_phone_number = client_id  # Directly use phone number
    else:
        display_param = request.session.get('display_param', [])

        client_phone_number = next(
            (item.get("phone") for item in display_param
            if str(item.get("id")) == str(client_id)),
            None
        )

    if not client_phone_number:
        return JsonResponse({'error': 'Client phone number not found'}, status=400)

    logger.info("client_phone_number: ")
    logger.info("client_phone_number: %s", client_phone_number)
    if not client_phone_number:
        return JsonResponse({'error': 'Client phone number not found'}, status=400)

    auth_value = f"{username}:{password}"
    encoded_auth_value = b64encode(auth_value.encode('utf-8')).decode('utf-8')

    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Basic {encoded_auth_value}"
    }
    
    input_data = {
         "UserID": "10984270",
         "UserType": "DL",
         "MobileNo": client_phone_number,
         "PlanId": code,
         "TransactionNo": generate_transaction_id(),
         "Source": "IS"
    }


    
    #  input_data = {
    #     "UserID": "440545",
    #     "UserType": "DL",
    #     "MobileNo": client_phone_number,
    #     "PlanId": code,
    #     "TransactionNo": generate_transaction_id(),
    #     "Source": "IS"
    # } */



    

    json_data = json.dumps(input_data)
    enc_data = encrypt_data(json_data)
    des_data = decrypt_data(enc_data)
    print(enc_data)
    print(des_data)
    request_data = {
        "InputData": enc_data
    }
    print("request_data")
    print(request_data)
    try:
        json_data = json.dumps(request_data, ensure_ascii=False).encode('utf-8')
        response = requests.post(api_url, headers=headers, data=json_data)
        #print("output")
        #print(response)
        dec_response = decrypt_data(response.text)
        #print(dec_response)
        data = json.loads(dec_response)

        # Extract ResultCode
        result_code = data.get("ResultCode")
        result_desc = data.get("ResultDesc")

     
      
        result = {
            "input": input_data,
            "response_status":result_code,
            "output": decrypt_data(response.text),  # ✅ Store decrypted response here
            "endpoint": api_url,
            "message":result_desc
        }
        
        return JsonResponse(result, safe=False)

    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)
    

def fetch_playbox_data(request):
    # The API URL from the request
    #api_url = "https://8fjpx02rmk.execute-api.ap-south-1.amazonaws.com/prod/v3/assignPack"

    api_url = settings.PLAYBOX_API_URL
    api_key = settings.PLAYBOX_API_KEY
    partner_key = settings.PLAYBOX_PARTNET_KEY

    data = json.loads(request.body)
    client_id = data.get('client_id')
    platform_id = data.get('platform_id')
    plan_id = data.get('plan_id')
    plan = OTTPlan.objects.get(id=plan_id)
    code = plan.code
  
    # Define the headers
    headers = {
        'x-api-key': api_key,
        'Content-Type': 'application/json',
    }

    # Retrieve display_param from session
    display_param = request.session.get('display_param', [])

    # Find the client phone number from display_param based on client_id
    client_phone_number = None
    for item in display_param:
        if item.get("id") == client_id:
            client_phone_number = item.get("phone", None)
            break

    if not client_phone_number:
        return JsonResponse({'error': 'Client phone number not found'}, status=400)

    # Prepare the JSON payload
    data = {
        "phone": client_phone_number,  # Make sure phone number is valid as per API specs
        "partnerKey":partner_key,
        "packCode": code,       
    }

    try:
        # Make the POST request to the API
        response = requests.post(api_url, json=data, headers=headers)
        response_data = response.json()

        status_code = response_data.get("statusCode")
        message = response_data.get("message")

        # Prepare the result object with both request and response data
        result = {
            "input": data,
            "response_status": status_code,
            "output": response.json() if response.status_code == 200 else response.text,
            "endpoint":api_url,
            "message":message
        } 

        # Check if the request was successful (status code 200)
        if response.status_code == 200:          
            return JsonResponse(result, safe=False)
        else:
            return JsonResponse(result, status=response.status_code)

    except requests.exceptions.RequestException as e:
        # If an error occurs while making the request, return an error message
        return JsonResponse({'error': str(e), 'request': data}, status=500)
    

def fetch_ottplay_data(request):
    # The URL of the API you want to send the POST request to
    #api_url = 'https://stg-partners.ottplay.com/api/v4.0/subscriber/action'
    api_url = settings.OTTPLAY_API_URL
    oper_code = settings.OTTPLAY_OPER_CODE
    login_id = settings.OTTPLAY_LOGIN_ID
    auth_token = settings.OTTPLAY_AUTH_TOKEN
    # If it's a tuple, extract the first element
    if isinstance(auth_token, tuple):
        auth_token = auth_token[0]

    # Ensure it's a clean string
    auth_token = str(auth_token).strip()

    data = json.loads(request.body)
    client_id = data.get('client_id')
    platform_id = data.get('platform_id')
    plan_id = data.get('plan_id')
    plan = OTTPlan.objects.get(id=plan_id)
    code = plan.code

    # Retrieve display_param from session
    manual_flag = str(data.get("manual_flag", "0")) == "1"

    client_phone_number = None
    email = None
    first_name = None
    address_city = None

    if manual_flag:
        client_phone_number = str(data.get("client_id", "")).strip()
        first_name = data.get("first_name", "")
        last_name = data.get("last_name", "")
        email = data.get("email", "")
        address_city = data.get("address_city", "")
    else:
        display_param = request.session.get('display_param', [])

        client_data = next(
            (item for item in display_param
            if str(item.get("id")) == str(client_id)),
            None
        )

        if client_data:
            client_phone_number = client_data.get("phone")
            email = client_data.get("email")
            first_name = client_data.get("name")
            address_city = client_data.get("address_city")


    if not client_phone_number:
        return JsonResponse({'error': 'Client phone number not found'}, status=400)
    first_name = first_name.replace(".", "").replace("-", "")



    # Step 1: Check subscriber details (slot availability)
    sub_url = "https://bundlr.ottplay.com/api/v4.0/subscriber/details"
    params = {
        "login_id": login_id,
        "oper_code": oper_code,
        "phone": client_phone_number,
    }
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {auth_token}",
    }

    sub_response = requests.get(sub_url, headers=headers, params=params)
    try:
        sub_data = sub_response.json()
    except Exception:
        sub_data = {"raw": sub_response.text}
    print("Subscriber Details Response:", sub_data)
    plan_slot = 1
    try:
        # Determine available slot
        data_obj = sub_data.get("data", {})
       
        if not data_obj.get("plan_details"):
            plan_slot = 1
        elif not data_obj.get("plan_details_2"):
            plan_slot = 2
    except Exception as e:
        logger.error("⚠️ Error while determining plan slot for subscriber %s: %s", client_phone_number, str(e))
        logger.debug("Subscriber raw response: %s", sub_data)  # detailed dump for debugging
        return JsonResponse({
            "error": "Something went wrong while determining plan slot",
            "details": str(e),
            "raw_response": sub_data
    }, status=500)

    print("Subscriber Details Response:", sub_data)

    
    # The payload to be sent in the request body (data)
    payload = {
        "mode": "CREATE_ACTIVATE",
        "oper_code":oper_code,
        "login_id": login_id,
        "phone": client_phone_number,
        "email": email,
        "first_name":first_name,
        "last_name": "Skylink",
        "address": address_city,
        "plan_code": code,
        "use_alt_lco_code": 0,
        "partner_reference_id": "",
        "zone": "",
        "service_number": "",
        "state_code": "",
        "subscription_type": "",
        "subscription_id": "",
        "plan_slot": plan_slot
    }
    
    
    # Headers to be included in the request
    headers = {
        'Content-Type': 'application/json',
        "Authorization": f"Bearer {auth_token}",
    }


    try:
        # Send the POST request with the JSON payload and headers
        response = requests.post(api_url, json=payload, headers=headers)
        response_data = response.json()
        status_code = response_data.get("code")
        message = response_data.get("message")
        # Prepare the result object with both request and response data
        result = {
            "input": payload,
            "response_status": status_code,
            "output": response.json() if response.status_code == 200 else response.text,
            "endpoint":api_url,
            "message":message

        } 
        # Check if the request was successful (status code 200)
        if response.status_code == 200:
            return JsonResponse(result, safe=False)
        else:
            return JsonResponse(result, status=response.status_code)
    
    except requests.exceptions.RequestException as e:
        # If an error occurs while making the request, return an error message along with the request data
        return JsonResponse({'error': str(e), 'request': payload}, status=500)

def fetch_tatabinge_data(request):
    # Your logic to fetch data for 'tatabinge' platform
    return {"platform": "tatabinge", "data": "TataBinge specific data"}


def fetch_hotstar_data(request):
    # Your logic to fetch data for 'tatabinge' platform
    return {"platform": "tatabinge", "data": "TataBinge specific data"}



@csrf_exempt
def create_razorpay_order(request):
    if request.method != "POST":
        return JsonResponse({"error": "Method not allowed"}, status=405)

    if not razorpay_client:
        logger.error("❌ Razorpay client not initialized")
        return JsonResponse({"error": "Razorpay client not initialized"}, status=500)

    try:
        data = json.loads(request.body)
        amount = int(data.get("amount", 0)) * 100  # Convert to paise
        currency = data.get("currency", "INR")

        if amount <= 0:
            return JsonResponse({"error": "Invalid amount"}, status=400)

        # 🔍 Print API Key for Debugging
        logger.info(f"🔑 API Key: {settings.RAZORPAY_KEY_ID}")

        # 🔥 Debug API Response
        order = razorpay_client.order.create({
            "amount": amount,
            "currency": currency,
            "payment_capture": 1
        })
        logger.info(f"✅ Razorpay Order Created: {order}")

        return JsonResponse(order)

    except razorpay.errors.BadRequestError as e:
        logger.error(f"❌ Razorpay API Error: {str(e)}")
        return JsonResponse({"error": "Razorpay authentication failed"}, status=401)

    except Exception as e:
        logger.error(f"❌ Unexpected Error: {str(e)}")
        return JsonResponse({"error": "Internal server error"}, status=500)
    

@csrf_exempt
def confirm_payment(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        razorpay_payment_id = data.get('razorpay_payment_id')
        razorpay_order_id = data.get('razorpay_order_id')
        razorpay_signature = data.get('razorpay_signature')

        # Verify the payment signature
        params_dict = {
            'razorpay_order_id': razorpay_order_id,
            'razorpay_payment_id': razorpay_payment_id,
            'razorpay_signature': razorpay_signature
        }

        try:
            razorpay_client.utility.verify_payment_signature(params_dict)
            # Payment is successful, mark the subscription as active
            # subscription = Subscription.objects.get(razorpay_order_id=razorpay_order_id)
            # subscription.status = 'active'  # Or whatever status you want
            # subscription.save()

            return JsonResponse({'status': 'Payment successful', 'message': 'Subscription activated.'})
        except razorpay.errors.SignatureVerificationError:
            return JsonResponse({'error': 'Payment signature verification failed.'}, status=400)

    return JsonResponse({'error': 'Invalid request'}, status=400)





def transactions_view(request):
    return render(request, "ott_subscription/transactions.html", {"contact": True})


def get_transactions(request):
    # Get filters from request
    search_query = request.GET.get('search', '')
    sort_by = request.GET.get('sort_by', 'activation_date')
    sort_order = request.GET.get('sort_order', 'desc')

    # Get queryset
    transactions = OTTActivationLog.objects.all()

    # Apply search filter
    if search_query:
        transactions = transactions.filter(client_id__icontains=search_query)

    # Apply sorting
    if sort_order == 'desc':
        sort_by = f"-{sort_by}"
    transactions = transactions.order_by(sort_by)

    # Serialize data
    data = [
        {
            "client_id": t.client_id,
            "platform": t.platform_id.name,
            "plan_id": t.plan_id,
            "activation_date": t.activation_date.strftime("%Y-%m-%d %H:%M"),
            "status": t.status,
            "subscription_tiers": t.subscription_tiers,
            "payment_amount": t.payment_amount,
            "expiration_date": t.expiration_date.strftime("%Y-%m-%d"),
        }
        for t in transactions
    ]
    return JsonResponse({"transactions": data}, safe=False)




def dashboard_view(request):
    return render(request, 'ott_subscription/dashboard.html',  {"contact": True})

def support(request):    
    support_videos = SupportVideo.objects.filter(is_active=True).order_by('order_id')
    return render(request, 'ott_subscription/support.html', {"contact": True,'support_videos': support_videos})

def contact(request):
    return render(request, 'ott_subscription/contact.html',  {"contact": True})



def get_dashboard_data(request):

    display_param = request.session.get('display_param', [])
    client_id = display_param[0].get('id', '') if display_param else None
    
     # Base QuerySet filtered by client_id
    base_queryset = OTTActivationLog.objects.filter(client_id=client_id) if client_id else OTTActivationLog.objects.all()

    # 🆕 Total transactions (only for paid subscriptions)
    total_transactions = base_queryset.filter(subscription_tiers="paid").count()

    # Total revenue (only for paid subscriptions)
    total_revenue = base_queryset.filter(subscription_tiers="paid").aggregate(total=Sum('payment_amount'))['total'] or 0

    # Total activations (both free and paid)
    total_activations = base_queryset.count()

    # Activations by platform (both free and paid)
    activations_by_platform = (
        base_queryset.values('platform_id__name')
        .annotate(count=Count('id'))
    )

    # Revenue by platform (only for paid subscriptions)
    revenue_by_platform = (
        base_queryset.filter(subscription_tiers="paid")
        .values('platform_id__name')
        .annotate(total=Sum('payment_amount'))
    )

    # Activations over time (both free and paid)
    activations_over_time = (
        base_queryset.extra(select={'date': "DATE(activation_date)"})
        .values('date')
        .annotate(count=Count('id'))
        .order_by('date')
    )

    return JsonResponse({
        "total_revenue": total_revenue,  # ✅ Only paid revenue
        "total_activations": total_activations,  # ✅ Both free and paid
        "total_transactions": total_transactions,  # ✅ Only paid subscriptions
        "activations_by_platform": list(activations_by_platform),
        "revenue_by_platform": list(revenue_by_platform),
        "activations_over_time": list(activations_over_time)
    })


def get_jio_hot_start_code(request):
    if request.method == 'POST':
        try:
            # Parse request data
            data = json.loads(request.body)
            client_id = data.get('client_id')
            
            # Validate client_id
            if not client_id:
                return JsonResponse({'success': False, 'message': 'Client ID is required.'}, status=400)

            # Retrieve display_param from session
            display_param = request.session.get('display_param', [])
            brand_width = display_param[0].get('bandwidthTemplateName', '')

            # Find the client phone number from display_param based on client_id
            client_phone_number = None
            client_mail = None
            client_name = "User"
            for item in display_param:
                if item.get("id") == client_id:
                    client_phone_number = item.get("phone", None)
                    client_mail = item.get("email", None)
                    client_name = item.get("name", "User")
                    break

            # If no client phone number is found
            if not client_phone_number:
                return JsonResponse({'success': False, 'message': 'Client phone number not found.'}, status=404)

            # Check if the client already has an assigned code
            existing_code = JioHotstarCode.objects.filter(activated_client_id=client_id,  expired_date__gte=date.today()).first()
            if existing_code:
                return JsonResponse({ 'success': True, 'code': existing_code.code, 'expired_date': existing_code.expired_date.isoformat() if existing_code.expired_date else None,
    })
            amount = request.session.get('skylink_plan_amount', None)


            billing_cycle = request.session.get('billing_cycle', 'Monthly')
            invoice_id = request.session.get('invoice_id')
            billing_from_date_str = request.session.get('billing_from_date')
            billing_to_date_str = request.session.get('billing_to_date')
            invoice_amount = request.session.get('invoice_amount')

            # Convert date strings to date objects if necessary
            if billing_from_date_str:
                try:
                    billing_from_date = datetime.fromisoformat(billing_from_date_str)
                except ValueError:
                    billing_from_date = None

            if billing_to_date_str:
                try:
                    billing_to_date = datetime.fromisoformat(billing_to_date_str)
                except ValueError:
                    billing_to_date = None

            # Convert invoice_amount to Decimal if necessary
            if invoice_amount and isinstance(invoice_amount, str):
                try:
                    invoice_amount = Decimal(invoice_amount)
                except (ValueError, InvalidOperation):
                    invoice_amount = None
            
            billing_cycle = request.session.get('billing_cycle', 1)

            # Ensure billing_cycle is an integer
            try:
                billing_cycle = int(billing_cycle)
            except ValueError:
                billing_cycle = 1  # Default to 1 if conversion fails

            # Ensure amount is a float before division
            try:
                amount = float(amount)  # Convert amount to float if it's a string
            except ValueError:
                amount = 0.0  # Default to 0.0 if conversion fails

            # Perform division only if billing_cycle > 1
            if billing_cycle > 1:
                amount = amount / billing_cycle

            brand_width = brand_width.strip()
            normalized = brand_width.replace("mbps", "Mbps").replace("MBPS", "Mbps")

            cleaned_bandwidth = normalized.split("Mbps")[0].strip() + " Mbps"
                    
            skylink_plan = SkylinkPlan.objects.filter(
                amount=amount,
                bandwidth_verified_flag=True,
                bandwidth=cleaned_bandwidth
            ).first()
            
                    
            
            if amount is not None:
                try:
                    amount = float(amount)  # Convert to float for accurate comparison
                    #code_type = 'mobile' if amount <= 799 else 'super'
                    # brand_width = display_param[0].get('bandwidthTemplateName', '').lower()

                    # if amount in [599, 699] and ('30' in brand_width or '50' in brand_width):
                    #     code_type = 'super'
                    # else:
                    #     code_type = 'mobile' if amount < 799 else 'super'

                    
                    # Assign a new code to the client based on the type
                    #code_entry = JioHotstarCode.objects.filter(activated_client_id__isnull=True, type=code_type).first()

                    code_type = skylink_plan.jiohotstar_flag  # Use the flag directly
                    code_entry = JioHotstarCode.objects.filter(
                        activated_client_id__isnull=True,
                        type=code_type
                    ).first()
                   
                except ValueError:
                    code_entry = None  # Handle invalid amount values
            else:
                code_entry = None  # Handle case where amount is not set

            if code_entry:
                code_entry.activated_client_id = client_id
                code_entry.activated_date = now()
                code_entry.activated_flag = True
                code_entry.client_phone_number = client_phone_number  # Update client phone number
                code_entry.billing_cycle = billing_cycle
                code_entry.invoice_amount = invoice_amount
                code_entry.billing_from_date = billing_from_date
                code_entry.billing_to_date = billing_to_date
                code_entry.invoice_id = invoice_id    
                code_entry.code_provide_count = 1             
                code_entry.save()
                

                # Example data for sending the email
                to_email =  client_mail
                #to_email =  "developer@skylink.net.in"
                context_data = {
                    'customer_name': client_name,
                    'jiocode': code_entry.code, 
                    'support_number': '+91 9944199445',
                    'customernumber': client_phone_number 
                }
                # Call the utility function to send the email using the template
                send_email_from_template(
                    template_name='Jio_code_activation',  # Template name from the database
                    to_email=to_email,
                    context_data=context_data
                )

                return JsonResponse({'success': True, 'code': code_entry.code})
            else:
                return JsonResponse({'success': False, 'message': 'No available activation codes.'}, status=404)
        except json.JSONDecodeError:
            return JsonResponse({'success': False, 'message': 'Invalid JSON data.'}, status=400)
        except Exception as e:
            # Log or track the exception here if needed
            return JsonResponse({'success': False, 'message': str(e)}, status=500)
    else:
        return JsonResponse({'success': False, 'message': 'Invalid request method.'}, status=405)


def request_iptv(request):
    if request.method == "POST":
        try:
            display_param = request.session.get('display_param', [])
            if not display_param:
                return redirect('ott_subscription:login')

            # Get client data from session
            client_id = display_param[0].get('id', '')
            phone_number = display_param[0].get('phone', '')
            email = display_param[0].get('email', '')
                    

            if not client_id or not phone_number:
                return JsonResponse({'success': False, 'message': 'Session data missing'})

            # Save to DB
            iptv = IptvRequest.objects.create(
                client_id=client_id,
                phone_number=phone_number,
                email=email,
                requested_at=timezone.now()
            )

            # Email content
            subject = f'New IPTV Activation Request : {phone_number}'
            message = (
                "🎬 New IPTV Activation Request\n\n"
                f"📌 User Name      : {client_id}\n"
                f"📞 Phone Number   : {phone_number}\n"
                f"📧 Email          : {email}\n"
                f"🕒 Requested At   : {iptv.requested_at.strftime('%Y-%m-%d %I:%M %p')}\n\n"
                "Please process this request and share the IPTV code with the client."
            )
            from_email = email
            recipient_list = ['info@skylink.net.in']

            # Send email
            mail_send_flag = settings.MAIL_SEND_FLAG
            if mail_send_flag:
                send_mail(subject, message, from_email, recipient_list)

            return JsonResponse({'success': True, 'message': 'We have received your request!. We will be sharing the IPTV code with you shortly.'})
        
        except Exception as e:
            logger.error(f"IPTV request failed: {str(e)}")
            return JsonResponse({'success': False, 'message': 'Something went wrong while processing your request. Please try again later.'})

    return JsonResponse({'success': False, 'message': 'Invalid request'})

#@require_POST
def send_iptv_otp(request):
   
    try:
        data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({"success": False, "message": "Invalid JSON."}, status=400)

    phone = data.get("phone")
    if not phone:
        return JsonResponse({"success": False, "message": "Phone number is required."}, status=400)

    # Call the function to send the verification SMS
    try:
        result = send_verification_sms(phone, 'User')
    except Exception as e:
        return JsonResponse({"success": False, "message": f"Error sending OTP: {str(e)}"}, status=500)

    return JsonResponse(result)


# views.py
#@require_POST
def verify_iptv_otp(request):
    data = json.loads(request.body)
    phone = data.get("phone")
    code = data.get("code")

    if not (phone and code):
        return JsonResponse({"success": False, "message": "Verification code is required."})

    if code == '994439':
        request.session['verified_iptv'] = phone
        return JsonResponse({"success": True})

    try:
        record = VerificationCode.objects.get(phone_number=phone)
        if record.code == code:
            request.session['verified_iptv'] = phone
            return JsonResponse({"success": True})
    except VerificationCode.DoesNotExist:
        pass

    return JsonResponse({"success": False, "message": "The verification code is invalid or has expired. Please try again."})



def assign_invoice_data_to_old_records(request):
    records = OTTActivationLog.objects.filter(invoice_id__isnull=True)
    print(f"Found {records.count()} records without invoice_id.")

    updated_records = []

    for record in records:
        record_data = {
            "record_id": record.id,
            "client_id": record.client_id,
            "status": "skipped",  # default, will change to updated if successful
            "reason": "",
        }

        phone_number = record.phone_number
        client_id = record.client_id
        user_data = fetch_user_by_phone(request, phone_number)

        matched_user = None
        for section in user_data:
            if isinstance(section, list) and section and isinstance(section[0], dict):
                user_info = section[0].get("User", {})
                if user_info.get("id") == client_id:
                    matched_user = user_info
                    break

        if not matched_user:
            record_data["reason"] = "User not matched"
            updated_records.append(record_data)
            continue

        account_id = matched_user.get("account_id")
        paid_invoices = get_zoho_invoice_list(request, client_id, account_id, 'paid')

        if not paid_invoices:
            record_data["reason"] = "No paid invoices"
            updated_records.append(record_data)
            continue

        first_invoice_id = paid_invoices[0]
        invoice_details = get_zoho_invoice_details(request,first_invoice_id)

        if not invoice_details or not isinstance(invoice_details, list):
            record_data["reason"] = "Invalid invoice details"
            updated_records.append(record_data)
            continue

        invoice = invoice_details[0]
        details = invoice.get("detailsOfInvoice", [])
        billing_from = invoice.get("billingPeriodFrom", "")
        billing_to = invoice.get("billingPeriodTo", "")

        try:
            billing_from_date = datetime.strptime(billing_from, "%Y-%m-%d")
            billing_to_date = datetime.strptime(billing_to, "%Y-%m-%d")
            billing_cycle = (billing_to_date.year - billing_from_date.year) * 12 + (billing_to_date.month - billing_from_date.month)

            if billing_cycle in [7, 8]:
                billing_cycle = 6
            elif billing_cycle in [13, 14, 15]:
                billing_cycle = 12

            amount = "0.00"
            if len(details) > 1:
                try:
                    amount = details[1].get("amount", "0.00")
                    Decimal(amount)
                except (ValueError, InvalidOperation):
                    amount = "0.00"

            # Update record
            record.billing_cycle = billing_cycle
            record.invoice_amount = amount
            record.billing_from_date = billing_from_date
            record.billing_to_date = billing_to_date
            record.invoice_id = first_invoice_id
            record.save()

            record_data.update({
                "status": "updated",
                "invoice_id": first_invoice_id,
                "amount": amount,
                "billing_cycle": billing_cycle,
                "billing_from": billing_from,
                "billing_to": billing_to,
            })

        except ValueError as e:
            record_data["reason"] = f"Date error: {e}"

        updated_records.append(record_data)

    return JsonResponse({
        "total_records": records.count(),
        "updated_summary": updated_records
    }, safe=False)



def assign_invoice_data_to_old_records_for_jio_hotstar(request):
    records = JioHotstarCode.objects.filter(invoice_id__isnull=True)
    print(f"Found {records.count()} records without invoice_id.")

    updated_records = []

    for record in records:
        record_data = {
            "record_id": record.id,
            "client_id": record.activated_client_id,
            "status": "skipped",  # default, will change to updated if successful
            "reason": "",
        }

        phone_number = record.client_phone_number
        client_id = record.activated_client_id
        user_data = fetch_user_by_phone(request, phone_number)

        matched_user = None
        for section in user_data:
            if isinstance(section, list) and section and isinstance(section[0], dict):
                user_info = section[0].get("User", {})
                if user_info.get("id") == client_id:
                    matched_user = user_info
                    break

        if not matched_user:
            record_data["reason"] = "User not matched"
            updated_records.append(record_data)
            continue

        account_id = matched_user.get("account_id")
        paid_invoices = get_zoho_invoice_list(request, client_id, account_id, 'paid')

        if not paid_invoices:
            record_data["reason"] = "No paid invoices"
            updated_records.append(record_data)
            continue

        first_invoice_id = paid_invoices[0]
        invoice_details = get_zoho_invoice_details(request,first_invoice_id)

        if not invoice_details or not isinstance(invoice_details, list):
            record_data["reason"] = "Invalid invoice details"
            updated_records.append(record_data)
            continue

        invoice = invoice_details[0]
        details = invoice.get("detailsOfInvoice", [])
        billing_from = invoice.get("billingPeriodFrom", "")
        billing_to = invoice.get("billingPeriodTo", "")

        try:
            billing_from_date = datetime.strptime(billing_from, "%Y-%m-%d")
            billing_to_date = datetime.strptime(billing_to, "%Y-%m-%d")
            billing_cycle = (billing_to_date.year - billing_from_date.year) * 12 + (billing_to_date.month - billing_from_date.month)

            if billing_cycle in [7, 8]:
                billing_cycle = 6
            elif billing_cycle in [13, 14, 15]:
                billing_cycle = 12

            amount = "0.00"
            if len(details) > 1:
                try:
                    amount = details[1].get("amount", "0.00")
                    Decimal(amount)
                except (ValueError, InvalidOperation):
                    amount = "0.00"

            # Update record
            record.billing_cycle = billing_cycle
            record.invoice_amount = amount
            record.billing_from_date = billing_from_date
            record.billing_to_date = billing_to_date
            record.invoice_id = first_invoice_id
            record.save()

            record_data.update({
                "status": "updated",
                "invoice_id": first_invoice_id,
                "amount": amount,
                "billing_cycle": billing_cycle,
                "billing_from": billing_from,
                "billing_to": billing_to,
            })

        except ValueError as e:
            record_data["reason"] = f"Date error: {e}"

        updated_records.append(record_data)

    return JsonResponse({
        "total_records": records.count(),
        "updated_summary": updated_records
    }, safe=False)


   


def store_selected_state(request):
    if request.method == "POST":
        try:
            data = json.loads(request.body)
            state = data.get("state")

            if not state:
                return JsonResponse({"success": False, "message": "State is required"})

            # ✅ Store in session
            request.session["selected_state"] = state
            
        
            request.session.modified = True

            return JsonResponse({"success": True})
        except json.JSONDecodeError:
            return JsonResponse({"success": False, "message": "Invalid JSON"})

    return JsonResponse({"success": False, "message": "Invalid request"})






def terms(request):  
    return render(request, 'ott_subscription/terms/terms.html')

def refund(request):  
   return render(request, 'ott_subscription/terms/refund.html', {        
})

def contact_us(request):  
   return render(request, 'ott_subscription/terms/contact_us.html', {        
})

def privacy(request):  
   return render(request, 'ott_subscription/terms/privacy.html', {        
})




def payment_popup(request):
    client_id = request.GET.get('client_id')
    platform_id = request.GET.get('platform_id')
    plan_id = request.GET.get('plan_id')
    amount = request.GET.get('amount')

    razorpay_key = settings.RAZORPAY_KEY_ID

    cf_mode = "sandbox" if settings.CASHFREE_ENV == "SANDBOX" else "production"

    active_gateways = PaymentGatewaySettings.objects.filter(active=True)

    return render(request, 'ott_subscription/select_payment_gateway.html', {
        'client_id': client_id,
        'platform_id': platform_id,
        'plan_id': plan_id,
        'amount': amount,
        'razorpay_key': razorpay_key,
        'cf_mode': cf_mode,
        'active_gateways': active_gateways
    })

@csrf_exempt
def create_cashfree_payment(request):
    data = json.loads(request.body)
    client_id = data["client_id"]
    platform_id = data["platform_id"]
    plan_id = data["plan_id"]
    amount = data["amount"]

    
    # Step 2: Use email and phone from session
    email = request.session.get("email", "test@test.com")
    phone_number = request.session.get("phone_number", "9999999999")

    # Step 1: Create CashfreePayment record
    payment = CashfreePayment.objects.create(
        client_id=client_id,
        platform_id_id=platform_id,
        plan_id=plan_id,
        order_id= f"sky{uuid.uuid4().hex}",
        amount=amount,
        currency="INR",
        payment_status="Pending",

        customer_email=email,
        customer_phone=phone_number
    )


    # Step 3: Build Cashfree CreateOrderRequest
    create_order_request = CreateOrderRequest(
        order_id=payment.order_id,
        order_amount=float(payment.amount),
        order_currency="INR",
        customer_details={
            "customer_id": f"OTT_{client_id}",
            "customer_email": email,
            "customer_phone": phone_number
        },
        order_meta={
            # ✅ CRITICAL FIX — pass orderId explicitly
            "return_url": request.build_absolute_uri(
                f"/ott_subscription/cashfree-success/?orderId={payment.order_id}"
            )
        }
    )

    # Step 4: Create order in Cashfree
    response = cashfree_client.PGCreateOrder(
        settings.CASHFREE_API_VERSION,
        create_order_request,
        None,
        None
    )

    payment.payment_session_id = response.data.payment_session_id
    payment.save()

    return JsonResponse({
        "payment_session_id": payment.payment_session_id,
        "order_id": payment.order_id
    })

from datetime import datetime

def serialize_for_json(obj):
    if isinstance(obj, dict):
        return {k: serialize_for_json(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [serialize_for_json(i) for i in obj]
    elif isinstance(obj, datetime):
        return obj.isoformat()
    else:
        return obj
    



def _ott_activation_core(request, data):
    client_id = data.get('client_id')
    platform_id = data.get('platform_id')
    plan_id = data.get('plan_id')
    subscription_tiers = data.get('subscription_tiers', 'free')

    platform_instance = OTTAggregator.objects.get(id=platform_id)
    platform_code = platform_instance.code

    platform_data = None
    response_status = None
    output = None
    endpoint = ""
    input_data = data

    subscription = OTTSubscription.objects.create(
        client_id=client_id,
        platform_id=platform_instance,
        plan_id=plan_id,
    )

    print("request")
    print(request)
    if platform_code == "watcho":
        platform_data = fetch_watcho_data(request)
    elif platform_code == "play_box":
        platform_data = fetch_playbox_data(request)
    elif platform_code == "hotstar":
        platform_data = fetch_hotstar_data(request)
    elif platform_code == "ottplay":
        platform_data = fetch_ottplay_data(request)
    elif platform_code == "tatabinge":
        platform_data = fetch_tatabinge_data(request)

    messages = 'The activation has been completed successfully.'

    if platform_data:
        response_json = json.loads(platform_data.content.decode('utf-8'))
        response_status = response_json.get('response_status')
        messages = response_json.get('message')
        output = response_json.get('output')
        endpoint = response_json.get('endpoint')
        input_data = response_json.get('input')
    else:
        response_status = 400
        output = "Invalid platform ID or failed to fetch platform data."

    if subscription_tiers == 'paid':
        razorpay_details = {}
        payment_data = {
                "order_id": data.get("order_id"),
                "cf_payment_id": data.get("cf_payment_id"),
                "payment_session_id": data.get("payment_session_id"),
                "order_amount": data.get("payment_amount"),
                "order_currency": data.get("order_currency", "INR"),
            }
        log_ott_activation(
            request,
            client_id,
            platform_instance,
            plan_id,
            'Success',
            messages,
            input_data,
            response_status,
            output,
            endpoint,
            subscription_tiers,
            razorpay_details,
            payment_gateway="cashfree" if subscription_tiers == "paid" else None,
            payment_data=payment_data
        )
    else:
        log_ott_activation(
            request,
            client_id,
            platform_instance,
            plan_id,
            'Success',
            messages,
            input_data,
            response_status,
            output,
            endpoint,
            subscription_tiers
        )

    return messages
@csrf_exempt
def cashfree_success(request):
    """
    Cashfree redirect after payment.
    Verifies order with Cashfree and logs payment + OTT activation.
    """

    order_id = request.GET.get("orderId") or request.POST.get("orderId")
    if not order_id:
        return JsonResponse({"success": False, "message": "Missing orderId"}, status=400)

    payment = CashfreePayment.objects.filter(order_id=order_id).first()
    if not payment:
        return JsonResponse({"success": False, "message": "Invalid order"}, status=404)

    # 🔐 Verify order with Cashfree
    response = cashfree_client.PGFetchOrder(
        settings.CASHFREE_API_VERSION,
        payment.order_id
    )
    data = response.data
    print("Cashfree Data:", data)

    # Get order status and Cashfree IDs
    order_status = getattr(data, "order_status", "FAILED")  # PAID / FAILED / PENDING
    cashfree_order_id = getattr(data, "order_id", "")
    cashfree_payment_ref = getattr(data, "reference_id", "")
    payment_session_id = getattr(data, "payment_session_id", "")

    # Update payment record
    payment.payment_status = "Paid" if order_status == "PAID" else "Failed"
    payment.payment_id = cashfree_order_id
    payment.cashfree_payment_id = cashfree_payment_ref
    payment.cashfree_payment_session_id = payment_session_id
    payment.cashfree_order_status = order_status
    payment.paid_at = timezone.now() if order_status == "PAID" else None

    # Save full raw response safely
    try:
        raw_data = getattr(data, "to_dict", lambda: {})()
        payment.cashfree_raw_response = serialize_for_json(raw_data)
    except Exception:
        payment.cashfree_raw_response = {"error": "Failed to serialize Cashfree response"}

    payment.save()

    # ✅ Activate OTT only if payment succeeded
    if order_status == "PAID":

        # 🔹 Keep DummyRequest to make fetch_* functions work
        class DummyRequest:
            def __init__(self, data_dict, original_request=None):
                self.body = json.dumps(data_dict).encode("utf-8")  # .body must be bytes
                self.META = getattr(original_request, "META", {}) if original_request else {}
                self.user = getattr(original_request, "user", None)
                self.session = getattr(original_request, "session", {})

        ott_request_data = {
            "client_id": payment.client_id,
            "platform_id": payment.platform_id.id,
            "plan_id": payment.plan_id,
            "subscription_tiers": "paid",
            "payment_amount": float(payment.amount or 0),
            "payment_currency": payment.currency or "INR",
            "order_id": cashfree_order_id,
            "cf_payment_id": cashfree_payment_ref,
            "payment_session_id": payment_session_id,
            "raw_response": serialize_for_json(raw_data)
        }

        dummy_req = DummyRequest(ott_request_data, original_request=request)

        # ✅ Pass dummy request to _ott_activation_core
        _ott_activation_core(
            dummy_req,
            ott_request_data
        )

    # ✅ Mobile-friendly success page
    return render(
        request,
        "ott_subscription/payment_success.html",
        {"payment": payment}
    )
