#!/usr/bin/env python3
import os
import shutil
import tempfile
import pwd
import logging
import logging.handlers
import MySQLdb
import configparser
from datetime import datetime, timezone
import grp

CONFIG_FILE = "/etc/ngcp-reminder/reminder.conf"
LOG_FILE = "/var/log/ngcp/ngcp-reminder.log"
OWNERLOGFILE = "root"
OWNERTMPFILE = "asterisk"

# --- Logging setup -----------------------------------------------------------
SYSLOG_ADDRESS = '/dev/log'  # Standard Unix socket for local syslog

# Ensure proper permissions and ownership for the log file
if not os.path.exists(LOG_FILE):
    open(LOG_FILE, "w").close()
    uid = pwd.getpwnam(OWNERLOGFILE).pw_uid
    gid = grp.getgrnam('adm').gr_gid
    os.chown(LOG_FILE, uid, gid)
    os.chmod(LOG_FILE, 0o640)  # rw-r-----

logger = logging.getLogger("ngcp-reminder")
logger.setLevel(logging.INFO)

# Syslog handler (facility local0)
syslog_handler = logging.handlers.SysLogHandler(
    address=SYSLOG_ADDRESS,
    facility=logging.handlers.SysLogHandler.LOG_LOCAL0
)
syslog_formatter = logging.Formatter(
        'ngcp-reminder: [%(levelname)s] %(message)s')
syslog_handler.setFormatter(syslog_formatter)
logger.addHandler(syslog_handler)

# File handler (optional fallback)
file_handler = logging.FileHandler(LOG_FILE)
file_formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)


# --- Configuration reader ----------------------------------------------------
def read_config(path):
    """Read key=value configuration file without section headers."""
    parser = configparser.ConfigParser()
    with open(path) as f:
        data = f.read()
    parser.read_string("[DEFAULT]\n" + data)
    return parser["DEFAULT"]


# --- Database connection -----------------------------------------------------
def connect_db(cfg):
    """Establish a connection to the MySQL database."""
    try:
        db = MySQLdb.connect(
            host=cfg.get("dbhost", "localhost"),
            user=cfg.get("dbuser"),
            passwd=cfg.get("dbpassword"),
            db=cfg.get("database"),
        )
        return db
    except Exception as e:
        logger.error(f"Database connection failed: {e}")
        raise


# --- Call file creation ------------------------------------------------------
def create_call_file(
    cfg,
    username,
    domain,
    lang,
    recur,
    reminder_id,
    weekdays
):
    """Create an Asterisk .call file based on the reminder configuration."""
    tmpdir = cfg.get("tmpdir", "/tmp")
    spool = cfg.get("spool", "/var/spool/asterisk/outgoing")

    # Normalize PJSIP peer name
    peer = cfg.get("sip_peer", "sip_proxy_ep")
    if not peer.upper().startswith(("SIP/", "PJSIP/")):
        peer = f"PJSIP/{peer}"

    tmp_prefix = f"{username}__AT__{domain}."
    fd, tmp_filename = tempfile.mkstemp(
        prefix=tmp_prefix,
        dir=tmpdir,
        text=True
    )
    os.close(fd)

    try:
        # Write call file content
        with open(tmp_filename, "w") as f:
            f.write(f"Channel: {peer}/sip:{username}@{domain}\n")
            f.write(f"MaxRetries: {cfg.get('retries', '2')}\n")
            f.write(f"RetryTime: {cfg.get('retry_time', '60')}\n")
            f.write(f"WaitTime: {cfg.get('wait_time', '30')}\n")
            f.write("Extension: s\n")
            f.write(f"Context: {cfg.get('context', 'reminder')}\n")
            f.write("Priority: 1\n")
            f.write(f"Setvar: LANG={lang or 'en'}\n")

        # Set file ownership and permissions
        uid = pwd.getpwnam(OWNERTMPFILE).pw_uid
        gid = pwd.getpwnam(OWNERTMPFILE).pw_gid
        os.chown(tmp_filename, uid, gid)
        os.chmod(tmp_filename, 0o600)

        # Read back for logging
        with open(tmp_filename, "r") as f:
            content = f.read()

        final_path = os.path.join(spool, os.path.basename(tmp_filename))
        shutil.move(tmp_filename, final_path)

        logger.info(
            f"Created call file: {final_path}\n"
            "--- File content ---\n"
            f"{content}\n"
            "--------------------"
        )

    except Exception as e:
        logger.error(f"Error creating call file for {username}@{domain}: {e}")
        if os.path.exists(tmp_filename):
            os.remove(tmp_filename)


# --- Main logic --------------------------------------------------------------
def main():
    """Main execution logic for the NGCP reminder system."""
    cfg = read_config(CONFIG_FILE)
    weekdays = [
        w.strip()
        for w in cfg.get("weekdays", "2,3,4,5,6,7").split(",")
    ]

    db = connect_db(cfg)
    cur = db.cursor(MySQLdb.cursors.DictCursor)

    sql = """
    SELECT s.username, d.domain, r.recur, r.id, vup.value as lang
    FROM provisioning.voip_reminder r
    LEFT JOIN provisioning.voip_subscribers s ON r.subscriber_id = s.id
    LEFT JOIN provisioning.voip_domains d ON s.domain_id = d.id
    LEFT JOIN billing.v_subscriber_timezone tz ON tz.uuid = s.uuid
    LEFT JOIN provisioning.voip_usr_preferences vup
    ON r.subscriber_id=vup.subscriber_id
    LEFT JOIN provisioning.voip_preferences vp ON vp.id=vup.attribute_id
    WHERE r.active = 1
      AND vp.attribute = "language"
      AND r.time =
        time_format(CONVERT_TZ(now(), "localtime", tz.name), '%H:%i:00')
    UNION
    SELECT s.username, d.domain, r.recur, r.id, vdp.value as lang
    FROM provisioning.voip_reminder r
    LEFT JOIN provisioning.voip_subscribers s ON r.subscriber_id = s.id
    LEFT JOIN provisioning.voip_domains d ON s.domain_id = d.id
    LEFT JOIN billing.v_subscriber_timezone tz ON tz.uuid = s.uuid
    LEFT JOIN provisioning.voip_dom_preferences vdp ON vdp.domain_id=d.id
    LEFT JOIN provisioning.voip_preferences vp ON vp.id=vdp.attribute_id
    AND r.time =
        time_format(CONVERT_TZ(now(), "localtime", tz.name), '%H:%i:00')
    WHERE r.active = 1
      AND vp.attribute = "language"
    LIMIT 100;
    """

    cur.execute(sql)
    rows = cur.fetchall()

    for ref in rows:
        username = ref["username"]
        domain = ref["domain"]
        recur = ref["recur"]
        lang = ref.get("lang", "en")
        reminder_id = ref["id"]

        # Weekday check for recurring reminders
        if recur == "weekdays":
            wday = datetime.now(timezone.utc).isoweekday()
            if str(wday) not in weekdays:
                continue

        create_call_file(
            cfg,
            username,
            domain,
            lang,
            recur,
            reminder_id,
            weekdays
        )

        # Disable one-time reminders
        if recur == "never":
            cur2 = db.cursor()
            cur2.execute(
                "UPDATE voip_reminder SET active=0 WHERE id=%s",
                (reminder_id,)
            )
            cur2.close()
            db.commit()

    cur.close()
    db.close()
    logger.info("Reminder cycle completed successfully.")


# --- Entry point -------------------------------------------------------------
if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        logger.exception(f"Unhandled exception: {e}")
        raise
