# (c) cavaliba.com - sirene - sms.py

import base64
import os
import random
import re
import time
from datetime import datetime, timedelta

import requests
from celery import shared_task
from django.conf import settings
from django.utils import timezone

import app_home.cache as cache
from app_home.configuration import get_configuration
from app_home.log import WARNING, log2

from .models import SMSJournal


# ------------------------------------------------------------
@shared_task(ignore_result=True)
def task_send_sms(dests, data, aaa=None):
    """returns number of sms sent successfully"""

    cache.init()

    if len(data) == 0:
        return 0

    sent, failed = [], []
    for num in dests:
        (sent if sirene_send_sms(num, data, aaa=aaa) else failed).append(num)
    count = len(sent)

    # one event per send, all recipients listed (decision 2026-10-18); the SMS journal also
    # keeps each successful send
    extra = {"recipients": len(dests), "sent": sent, "failed": failed}
    if not failed:
        log2.ok("sms.send", count=count, extra=extra)
    else:
        log2.fail("sms.send", reason="partial" if count else "send_failed", count=count,
                  level=WARNING if count else None, extra=extra)  # fmt: skip
    return count


# -------------------------------------------------------------
def sms_encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc).encode()).decode()


def sms_decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc).decode()
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)


# ----------------------------------------------------------------------
# Add journal entry


def add_journal(mobile=None, sms=None, aaa=None):

    quota = get_sms_quota(aaa)
    entry = SMSJournal()

    try:
        entry.created_by = aaa["username"]
    except Exception:
        entry.created_by = "n/a"

    entry.mobile = mobile
    entry.quota = quota

    key = get_configuration("env", "CAVALIBA_CIPHER_KEY")
    if key:
        entry.content = sms_encode(key, sms)
    else:
        entry.content = "no encryption key, no content."

    entry.save()


def get_sms_quota(aaa=None):

    if "username" not in aaa:
        return 9999999

    period = timezone.now() - timedelta(days=1)
    sent = SMSJournal.objects.filter(created_by=aaa["username"], created_at__gte=period).count()

    quota = int(get_configuration("sirene", "SMS_QUOTA_PER_DAY"))

    sms_left = max(quota - sent, 0)

    return sms_left


# ----------------------------------------------------------------------
def sirene_send_sms(num, sms, aaa=None):
    """returns  True/False"""

    if not sms_check_valid_number(num):
        # error(domain="sms.send", data=f"Invalid SMS number {num}", aaa=aaa)
        return False

    mode = get_configuration("sirene", "SMS_MODE")

    # limit to 159 chars max
    sms_cut = sms[:153]
    if len(sms_cut) != len(sms):
        sms_cut += " (...)"

    if mode == "stdout":
        return sms_mode_stdout(num, sms_cut, aaa=aaa)

    if mode == "folder":
        return sms_mode_folder(num, sms_cut, aaa=aaa)

    if mode == "clicsecure":
        return sms_mode_clicsecure(num, sms_cut, aaa=aaa)

    # NEXT : add SMS providers

    return False


# ----------------------------------------------------------------------------
def sms_check_valid_number(num):

    if not re.match(r"^[\d\s+()]+$", num):
        return False

    # if not re.match(r'^^\d{10}$', num):
    #     #print("SMS: incorrect SMS number: ", num)
    #     return False

    return True


# ----------------------------------------------------------------------
def sms_mode_stdout(num, sms, aaa=None):

    print(f"SMS (STDOUT) - {num} - {sms}")

    add_journal(mobile=num, sms=sms, aaa=aaa)
    log2.debug("sms (stdout) %s", num)

    return True


# ----------------------------------------------------------------------
def sms_mode_folder(num, sms, aaa=None):

    # to folder

    now_msec = time.time()
    filename = str(now_msec)
    filename = str(datetime.today().strftime("%Y-%m-%d-%H%M%S"))
    filename += "-"
    alea = int(random.random() * 1000000000)
    filename += str(alea)
    filename += ".json"

    # sms_folder = get_configuration("sirene", "SMS_FOLDER")
    # path = sms_folder + "/" + filename

    sms_folder = settings.CAVALIBA_SMS_FOLDER
    path = os.path.join(str(sms_folder), filename)

    now = timezone.now()
    data = f"'timestamp': '{now}',\n'msec': {now_msec},\n'sms': '{num}',\n'message': '{sms}'\n"
    data = "{\n" + data + " }\n"
    f = open(path, "a")
    f.write(data)
    f.close()

    add_journal(mobile=num, sms=sms, aaa=aaa)
    log2.debug("sms (folder) %s", num)

    return True


# ----------------------------------------------------------------------
def sms_mode_clicsecure(num, sms, aaa=None):

    login = get_configuration("env", "CAVALIBA_SMS_LOGIN")
    password = get_configuration("env", "CAVALIBA_SMS_PASSWORD")
    url = get_configuration("env", "CAVALIBA_SMS_URL")

    query = f"{url}email={login}&pass={password}&numero={num}&message={sms}"
    r = requests.get(query)

    if r.text in ["80", "81", "92", "94"]:
        add_journal(mobile=num, sms=sms, aaa=aaa)
        log2.debug("sms (clicsecure) %s: %s", num, r.text)
        return True
    else:
        log2.debug("sms (clicsecure) %s failed: %s %s", num, r.status_code, r.text, level=WARNING)
        return False
