import json
import os

from django.conf import settings
from django.core.mail.backends.smtp import EmailBackend
from django.http import HttpResponse
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.utils.translation import gettext_lazy as _
from rest_framework.status import HTTP_417_EXPECTATION_FAILED
from twilio.rest import Client
from plivo import RestClient
# from nexmo import Client
import logging

import traceback
from datetime import datetime

from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMultiAlternatives
from django.template.loader import get_template

from Masterdata.models import EmailGateway
from .models import PermissionPattern, UserRole
from django.db.models import Q
from LockTrust.Constants import USER_TYPE
from twilio.base.exceptions import TwilioException
from rest_framework.exceptions import APIException, _get_error_details as error_details, PermissionDenied, \
    ValidationError
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
import requests

from .utils import generate_token

logger = logging.getLogger(__name__)

class MobileException(APIException):
    status_code = HTTP_417_EXPECTATION_FAILED
    default_detail = _('Invalid input.')
    default_code = 'invalid'

    def __init__(self, detail=None, code=None):
        if detail is None:
            detail = self.default_detail
        if code is None:
            code = self.default_code

        # For validation failures, we may collect many errors together,
        # so the details should always be coerced to a list if not already.
        if isinstance(detail, tuple):
            detail = list(detail)
        elif not isinstance(detail, dict) and not isinstance(detail, list):
            detail = [detail]

        self.detail = error_details(detail, code)


def update_permission(user):
    if user.user_role_id != 6:
        user_type = USER_TYPE[user.user_type]
        user_role = UserRole.objects.filter(rn=user_type)
        a = PermissionPattern.objects.filter(user_type__contains=user.user_type)
        values = a.values_list('id', flat=True)
        print(values)
        q_object = Q()
        for i in values:
            q_object &= Q(id=i)
        if not user_role:
            user_role = UserRole.objects.create(rn=user_type)
            user_role.up.add(*values)
            user_role.save()
            user_role.save()
            user_role.save()
            user_role.save()
            user.user_role = user_role
        else:
            user.user_role = user_role[0]
        user.save()


def send_otp(gatewayDetails, otp, mobile):
    client = Client(gatewayDetails.SID, gatewayDetails.token)
    message = client.messages.create(body=f'LockTrust -> Your one time password is {otp}',
                                     from_=gatewayDetails.phone_no,
                                     to=mobile,
                                     messaging_service_sid=gatewayDetails.MessageServiceID)
    return message.sid

    # client = Client(key=gatewayDetails.SID, secret=gatewayDetails.token)
    # message = client.sms.send_message({
    #     "from": gatewayDetails.phone_no,
    #     "to": mobile,
    #     "text": f'LockTrust -> Your one time password is {otp}',
    # })
    # return message.sid


def send_otp_by_plivo(gatewayDetails, otp, mobile):
    client = RestClient(gatewayDetails.SID, gatewayDetails.token)
    message_created = client.messages.create(
        src=gatewayDetails.phone_no,  # '72728436367',
        dst=mobile,
        text=f'LockTrust -> Your one time password is {otp}',
    )
    return message_created.api_id


# def call_for_otp_by_twilio(otp, mobile):
#     client = Client(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN)
#     response = 'media/response.xml'
#     with open(response, 'w+') as file:
#         var = file.read()
#         file.write(
#             f'<?xml version="1.0" encoding="UTF-8"?><Response><Say loop="2">Hello,This is lock trust.Your Login One '
#             f'Time Password Is:-  <prosody rate="70%"><say-as interpret-as="telephone"> '
#             f'{otp}</say-as></prosody></Say></Response>')
#     call = client.calls.create(
#         to=mobile,
#         from_=settings.TWILIO_PHONE_NO,
#         url=settings.URL + response
#     )
#     return call.status

def call_for_otp_by_twilio(gatewayDetails, otp, mobile):
    client = Client(gatewayDetails.SID, gatewayDetails.token)
    twilm = (f'<?xml version="1.0" encoding="UTF-8"?><Response><Say loop="2">Hello, Your One Time passcode Is:- '
             f' <prosody rate="50%"><say-as interpret-as="telephone"> {otp}</say-as></prosody></Say></Response>')
    call = client.calls.create(
        twiml=twilm,
        to=mobile,
        from_=gatewayDetails.phone_no
    )
    return call.status


def send_email_otp(name, otp, email):
    message = {
        'user': name,
        'site_name': "LockTrust Inc",
        'otp': otp,
    }
    mail_subject = "otp for locktrust verification"
    plain_text = get_template('email/email_otp_template.txt').render(message)
    htmly = get_template('email/email_otp_template.html').render(message)

    config = EmailGateway.objects.get(isActive=True)
    gateway_type = config.gateway_type
    if gateway_type == 1 or gateway_type == 3:
        send_email_brevo_smtp(config, message, mail_subject, email, 'email/email_otp_template.html', 'email/email_otp_template.txt')
    else:
        send_email_grid(config, message, mail_subject, email, 'email/email_otp_template.html', 'email/email_otp_template.txt')

    #msg = EmailMultiAlternatives(mail_subject, plain_text, settings.EMAIL_HOST_USER, [email])
    #msg.attach_alternative(htmly, "text/html")
    #msg.send()


def send_email(context, subject, tosend, html_template, txt_template):
    config = EmailGateway.objects.get(isActive=True)
    gateway_type = config.gateway_type
    if gateway_type == 1 or gateway_type == 3:
       return send_email_brevo_smtp(config, context, subject, tosend, html_template, txt_template)
    else:
       return send_email_grid(config, context, subject, tosend, html_template, txt_template)

def write_email_log(msg):
    try:
        log_dir = os.path.join(settings.BASE_DIR, 'logs')
        logger.info(f"Attempting to create/access log dir: {log_dir}")

        if not os.path.exists(log_dir):
            os.makedirs(log_dir)
            logger.info("Log directory created.")

        log_path = os.path.join(log_dir, 'email_log.txt')
        with open(log_path, 'a') as f:
            timestamp = datetime.now().strftime("[%Y-%m-%d %H:%M:%S] ")
            f.write(f"{timestamp}{msg}\n")
            logger.info(f"Written to log: {log_path}")
    except Exception as e:
        logger.error(f"Failed to write log: {e}")


def send_email_grid(config, context, subject, tosend, html_template, txt_template):
    # html_content = render_to_string(html_template, context)
    # plain_text_content = strip_tags(html_content)  # Remove HTML tags for plain text version
    #
    # message = Mail(
    #     from_email=config.senderEmail,
    #     to_emails='gaurav@locktrust.com',#tosend,
    #     subject=subject,
    #     # html_content=loader.get_template(html_template).template.source
    #     html_content=html_content,
    #     plain_text_content=plain_text_content,
    # )
    # sg = SendGridAPIClient(config.apiKey)
    # response = sg.send(message)
    # print(response.status_code, response.body, response.headers)
    # return response
    html_content = render_to_string(html_template, context)
    plain_text_content = strip_tags(html_content)
    # try:
    #     html_content = render_to_string(html_template, context)
    #     plain_text_content = strip_tags(html_content)
    #     log_msg = f"Html Content {html_content} | Plain Text: {plain_text_content}"
    #     write_email_log(log_msg)
    #     message = Mail(
    #         from_email=config.senderEmail,
    #         to_emails=tosend,  # don't hardcode email
    #         subject=subject,
    #         html_content=html_content,
    #         plain_text_content=plain_text_content,
    #     )
    #     sg = SendGridAPIClient(config.apiKey)
    #     response = sg.send(message)
    #     log_msg = f"Email sent to {tosend} | Status: {response.status_code} | Message: {response.body} "
    #     write_email_log(log_msg)
    #     print("Email sent:", response.status_code, response.body, response.headers)
    #     return response
    #
    # except Exception as e:
    #     import traceback
    #     print("Exception while sending email:", str(e))
    #     traceback.print_exc()
    #     log_msg = f"Email exception {e}"
    #     write_email_log(log_msg)
    #     return None

    payload = {
        "personalizations": [
            {
                "to": [{"email": tosend}]
            }
        ],
        "from": {"email": "support@locktrust.com"},
        "subject": subject,
        "content": [
            {"type": "text/plain", "value": plain_text_content},
            {"type": "text/html", "value": html_content}
        ]
    }

    headers = {
        'Authorization': f'Bearer {config.apiKey}',
        'Content-Type': 'application/json'
    }
    try:
        response = requests.post("https://api.sendgrid.com/v3/mail/send", headers=headers, json=payload)
        log_msg = f" email sent to {tosend} | Status: {response.status_code} | Response: {response.text}"
        write_email_log(log_msg)
        return response.status_code, response.text

    except Exception as e:
        log_msg = f"Email exception while sending OTP to {tosend} | Error: {str(e)}"
        write_email_log(log_msg)
        return None, str(e)

def send_email_brevo_smtp(config, context, subject, tosend, html_template, txt_template):
    connection = EmailBackend(host=config.host, port=int(config.port), username=config.username,
                              password=config.password, use_tls=True, fail_silently=True)
    mail_subject = subject
    plain_text = get_template(txt_template).render(context)
    htmly = get_template(html_template).render(context)

    msg = EmailMultiAlternatives(mail_subject, plain_text, settings.EMAIL_HOST_USER,
                                 [tosend], connection=connection)
    msg.attach_alternative(htmly, "text/html")
    msg.send()
    log_msg = f" email sent to {tosend} | Status: {msg.status_code} | Response: {msg.text}"
    write_email_log(log_msg)

def is_valid_uuid(value):
    import uuid
    try:
        uuid.UUID(str(value))
        return True
    except ValueError:
        return False


def send_otp_twilio(gatewayDetails, otp, mobile):
    try:
        client = Client(gatewayDetails.SID, gatewayDetails.token)
        message = client.messages.create(body=f'LockTrust, Your one time password is {otp}',
                                         from_=gatewayDetails.phone_no,
                                         to=mobile, messaging_service_sid=gatewayDetails.MessageServiceID)
        return message.sid
        # client = Client(key=gatewayDetails.SID, secret=gatewayDetails.token)
        # message = client.send_message({
        #     "from": 'Vonage APIs',
        #     "to": '+919579668524',
        #     "text": f'LockTrust -> Your one time password is {otp}',
        # })

        # return message.sid

    except Exception as e:
        # Handle other exceptions
        print(f"An error occurred: {str(e)}")
    # except TwilioException as e:
    #     print(e)
    #     raise MobileException({"mobile": e})


def GetDelivereactToken(clientID, secretKey):
    postData = {'client_id': clientID, 'client_secret': secretKey, 'audience': 'https://api.staging.deliverect.com',
                'grant_type': 'token'}
    headers = {
        "accept": "application/json",
        "content-type": "application/json"
    }
    res = requests.post('https://api.staging.deliverect.com/oauth/token', data=postData)
    print(f"Delivery Act request status: {res.json()}")
    if res.status_code >= 200 or res.status_code <= 299:
        return res.json()
    if res.status_code >= 400 or res.status_code <= 499:
        raise ValidationError(res.json())
    else:
        raise PermissionDenied(res.text)


def GetDelivereactLocations(accountID, Token):
    url = f'https://api.staging.deliverect.com/locations?sort=-_created&max_results=500&cursor=new&where={{"account":"{accountID}"}}'

    headers = {"accept": "application/json", "Authorization": f"Bearer {Token}"}

    res = requests.get(url, headers=headers)
    if res.status_code >= 200 or res.status_code <= 299:
        return res.json()
    if res.status_code >= 400 or res.status_code <= 499:
        raise ValidationError(res.json())
    else:
        raise PermissionDenied(res.text)


def data_encoding(data: str):
    return urlsafe_base64_encode(force_bytes(data))


def token_generate(data: str):
    return generate_token.make_token(data)


def GetChannels(accountID, Token):
    url = f'https://api.staging.deliverect.com/channelLinks?sort=-_created&max_results=500&cursor=new&where={{"account":"{accountID}"}}'

    headers = {"accept": "application/json", "Authorization": f"Bearer {Token}"}

    res = requests.get(url, headers=headers)
    if res.status_code >= 200 or res.status_code <= 299:
        return res.json()
    if res.status_code >= 400 or res.status_code <= 499:
        raise ValidationError(res.json())
    else:
        raise PermissionDenied(res.text)
