import json
import os
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

import database

CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "email_config.json")
EXAMPLE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "email_config.json.example")


def _load_config():
    if not os.path.exists(CONFIG_PATH):
        return None
    try:
        with open(CONFIG_PATH, encoding="utf-8") as f:
            config = json.load(f)
        required = ("smtp_host", "smtp_port", "smtp_user", "smtp_password", "from_email")
        if all(config.get(k) for k in required):
            return config
    except (json.JSONDecodeError, OSError):
        pass
    return None


def is_configured():
    return _load_config() is not None


def get_config_for_display():
    """Return config with password masked for the admin settings form."""
    config = _load_config()
    if not config:
        if os.path.exists(EXAMPLE_PATH):
            try:
                with open(EXAMPLE_PATH, encoding="utf-8") as f:
                    example = json.load(f)
                return {
                    "smtp_host": example.get("smtp_host", "smtp.gmail.com"),
                    "smtp_port": str(example.get("smtp_port", 587)),
                    "smtp_user": "",
                    "smtp_password": "",
                    "from_email": "",
                    "from_name": example.get("from_name", "Readpath Library"),
                    "use_tls": example.get("use_tls", True),
                    "use_ssl": example.get("use_ssl", False),
                }
            except (json.JSONDecodeError, OSError):
                pass
        return {
            "smtp_host": "smtp.gmail.com",
            "smtp_port": "587",
            "smtp_user": "",
            "smtp_password": "",
            "from_email": "",
            "from_name": "Readpath Library",
            "use_tls": True,
            "use_ssl": False,
        }
    return {
        "smtp_host": config.get("smtp_host", ""),
        "smtp_port": str(config.get("smtp_port", 587)),
        "smtp_user": config.get("smtp_user", ""),
        "smtp_password": "********" if config.get("smtp_password") else "",
        "from_email": config.get("from_email", ""),
        "from_name": config.get("from_name", "Readpath Library"),
        "use_tls": bool(config.get("use_tls", True)),
        "use_ssl": bool(config.get("use_ssl", False)),
    }


def save_config(smtp_host, smtp_port, smtp_user, smtp_password, from_email, from_name="Readpath Library", use_tls=True, use_ssl=False):
    smtp_host = (smtp_host or "").strip()
    smtp_user = (smtp_user or "").strip()
    from_email = (from_email or "").strip()
    from_name = (from_name or "Readpath Library").strip()

    try:
        smtp_port = int(smtp_port)
    except (TypeError, ValueError):
        return False, "SMTP port must be a number."

    if not smtp_host or not smtp_user or not from_email:
        return False, "SMTP host, username, and from email are required."

    existing = _load_config()
    password = (smtp_password or "").strip()
    if password in ("", "********") and existing:
        password = existing.get("smtp_password", "")
    if not password:
        return False, "SMTP password / app password is required."

    config = {
        "smtp_host": smtp_host,
        "smtp_port": smtp_port,
        "smtp_user": smtp_user,
        "smtp_password": password,
        "from_email": from_email,
        "from_name": from_name,
        "use_tls": bool(use_tls) and not bool(use_ssl),
        "use_ssl": bool(use_ssl),
    }

    try:
        with open(CONFIG_PATH, "w", encoding="utf-8") as f:
            json.dump(config, f, indent=2)
    except OSError as e:
        return False, f"Could not save email settings: {e}"

    return True, "Email settings saved."


def _build_message(config, to_address, subject, body):
    msg = MIMEMultipart()
    msg["From"] = config.get("from_name", "Readpath Library") + f" <{config['from_email']}>"
    msg["To"] = to_address
    msg["Subject"] = subject
    msg.attach(MIMEText(body, "plain"))
    return msg


def _send_via_smtp(config, msg, to_address):
    port = int(config["smtp_port"])
    use_ssl = bool(config.get("use_ssl")) or port == 465

    if use_ssl:
        with smtplib.SMTP_SSL(config["smtp_host"], port, timeout=20) as server:
            server.login(config["smtp_user"], config["smtp_password"])
            server.sendmail(config["from_email"], [to_address], msg.as_string())
    else:
        with smtplib.SMTP(config["smtp_host"], port, timeout=20) as server:
            server.ehlo()
            if config.get("use_tls", True):
                server.starttls()
                server.ehlo()
            server.login(config["smtp_user"], config["smtp_password"])
            server.sendmail(config["from_email"], [to_address], msg.as_string())


def send_email(to_address, subject, body):
    config = _load_config()
    if not config:
        return False, (
            "Email is not set up yet. Open Email Overdue → Setup Email and save your "
            "Gmail (or school mail) SMTP details first."
        )

    to_address = (to_address or "").strip()
    if not to_address or "@" not in to_address:
        return False, "Student has no valid email address on file."

    try:
        msg = _build_message(config, to_address, subject, body)
        _send_via_smtp(config, msg, to_address)
        return True, f"Email sent to {to_address}"
    except smtplib.SMTPAuthenticationError:
        return False, (
            "Login failed. For Gmail, use an App Password (not your normal password). "
            "Google Account → Security → 2-Step Verification → App passwords."
        )
    except Exception as e:
        return False, f"Email failed: {e}"


def test_connection(test_to_address):
    config = _load_config()
    if not config:
        return False, "Save email settings first."

    test_to_address = (test_to_address or "").strip()
    if not test_to_address or "@" not in test_to_address:
        return False, "Enter a valid test email address."

    body = (
        "This is a test email from Readpath Library LMS.\n\n"
        "If you received this, your email settings are working correctly."
    )
    return send_email(test_to_address, "Readpath Library — Test Email", body)


def get_user_email(user_id):
    with database.connect() as conn:
        cursor = conn.cursor()
        cursor.execute("SELECT email, username FROM users WHERE id=?", (user_id,))
        row = cursor.fetchone()
        if not row:
            return None, None
        return row[0], row[1]


def get_students_with_email():
    rows = []
    with database.connect() as conn:
        cursor = conn.cursor()
        cursor.execute("""
            SELECT id, username, student_number, email
            FROM users
            WHERE role='student'
            ORDER BY username ASC
        """)
        for uid, username, student_num, email in cursor.fetchall():
            rows.append({
                "user_id": uid,
                "username": username,
                "student_number": student_num or "-",
                "email": (email or "").strip(),
            })
    return rows


def send_custom_email_to_student(user_id, subject, message):
    import notifications

    email, username = get_user_email(user_id)
    if not email:
        return False, f"Student '{username}' has no email address saved."

    subject = (subject or "").strip() or "Message from Readpath Library"
    message = (message or "").strip()
    if not message:
        return False, "Message cannot be empty."

    body = [
        f"Dear {username},",
        "",
        message,
        "",
        "— Readpath Library Administration",
    ]
    ok, result = send_email(email, subject, "\n".join(body))
    if ok:
        notifications.create_notification(
            user_id,
            subject,
            message,
            notification_type="general",
        )
    return ok, result


def _mark_email_sent(user_id, loan_ids):
    if not loan_ids:
        return
    with database.connect() as conn:
        cursor = conn.cursor()
        placeholders = ",".join("?" * len(loan_ids))
        cursor.execute(f"""
            UPDATE notifications SET email_sent=1
            WHERE user_id=? AND related_loan_id IN ({placeholders})
            AND notification_type='overdue'
        """, (user_id, *loan_ids))
        conn.commit()


def send_overdue_reminder(user_id, overdue_items, extra_message=""):
    email, username = get_user_email(user_id)
    if not email:
        return False, "No email on file — in-app notification only."

    lines = [
        f"Dear {username},",
        "",
    ]
    if extra_message.strip():
        lines.extend([extra_message.strip(), ""])

    lines.extend([
        "This is a reminder from Readpath Library regarding overdue book(s):",
        "",
    ])
    loan_ids = []
    for item in overdue_items:
        lines.append(
            f"- {item['title']} (due {item['due_date']}, {item['days_late']} day(s) late, "
            f"fine ₱{item['fine_amount']:.0f})"
        )
        loan_ids.append(item["loan_id"])

    lines.extend([
        "",
        "Please return the book(s) to the library as soon as possible.",
        "Continued failure to return may result in suspension of borrowing privileges.",
        "",
        "Library Rules — Lost & Damaged Books apply for unreturned items.",
        "",
        "Thank you,",
        "Readpath Library Administration",
    ])

    ok, msg = send_email(email, "Overdue Book Reminder — Readpath Library", "\n".join(lines))
    if ok:
        _mark_email_sent(user_id, loan_ids)
    return ok, msg


def send_overdue_email_to_student(user_id, custom_message=""):
    """Send overdue reminder email to one student. Also creates in-app notification."""
    import notifications
    import circulation

    student = None
    for entry in circulation.get_overdue_students_grouped():
        if entry["user_id"] == user_id:
            student = entry
            break

    if not student:
        return False, "This student has no overdue books right now."

    if not student["email"]:
        return False, (
            f"Student '{student['username']}' has no email saved. "
            "Add their email in All Accounts or when registering."
        )

    items = [
        {
            "loan_id": book["loan_id"],
            "title": book["title"],
            "due_date": book["due_date"],
            "days_late": book["days_late"],
            "fine_amount": book["fine_amount"],
        }
        for book in student["books"]
    ]

    notifications.sync_overdue_alerts(user_id, send_email=False)

    ok, msg = send_overdue_reminder(user_id, items, custom_message)
    if ok:
        note = (custom_message or "").strip()
        if note:
            notifications.create_notification(
                user_id,
                "Overdue Book Reminder",
                note,
                notification_type="overdue",
            )
    return ok, msg


def build_default_overdue_message(student):
    """Pre-fill message text for the admin to edit before sending."""
    lines = [
        "Please return your overdue book(s) to the library as soon as possible:",
        "",
    ]
    total_fine = 0
    for book in student["books"]:
        lines.append(
            f"• {book['title']} — due {book['due_date']} "
            f"({book['days_late']} day(s) late, fine ₱{book['fine_amount']:.0f})"
        )
        total_fine += book["fine_amount"]
    lines.extend([
        "",
        f"Total overdue fine: ₱{total_fine:.0f}",
        "",
        "Return the book(s) to avoid further penalties.",
    ])
    return "\n".join(lines)


def send_overdue_to_all_students(extra_message=""):
    import notifications

    results = []
    with database.connect() as conn:
        cursor = conn.cursor()
        cursor.execute("""
            SELECT DISTINCT loans.user_id
            FROM loans
            WHERE loans.status='borrowed'
            AND loans.returned=0
            AND loans.due_date IS NOT NULL
            AND loans.due_date < date('now')
        """)
        user_ids = [row[0] for row in cursor.fetchall()]

    for uid in user_ids:
        items = notifications.sync_overdue_alerts(uid, send_email=False)
        if not items:
            continue

        email, username = get_user_email(uid)
        if not email:
            results.append({
                "user_id": uid,
                "username": username,
                "email": "(no email on file)",
                "books": len(items),
                "email_sent": False,
                "email_message": "Student has no email — in-app alert only.",
            })
            continue

        ok, msg = send_overdue_reminder(uid, items, extra_message)
        results.append({
            "user_id": uid,
            "username": username,
            "email": email,
            "books": len(items),
            "email_sent": ok,
            "email_message": msg,
        })

    return results
