# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Email Server Skill - Main entry point.

Re-exports the EmailServerSkill class and provides CLI commands.
"""

try:
    from . import EmailServerConfig, EmailServerSkill
except (ImportError, SystemError):
    # When loaded dynamically by skill loader, relative imports fail.
    # Import __init__.py directly by path.
    import importlib.util
    import os

    pkg_dir = os.path.dirname(os.path.abspath(__file__))
    init_path = os.path.join(pkg_dir, "__init__.py")
    spec = importlib.util.spec_from_file_location("email_server_pkg", init_path)
    mod = importlib.util.module_from_spec(spec)
    try:
        spec.loader.exec_module(mod)
        EmailServerSkill = mod.EmailServerSkill
        EmailServerConfig = mod.EmailServerConfig
    except Exception:
        # email_server has heavy deps (aiosmtpd etc) — graceful degrade
        EmailServerSkill = None
        EmailServerConfig = None

__all__ = ["EmailServerSkill", "EmailServerConfig", "register"]


def register(agent, config):
    """Register the email server skill with an agent."""
    return EmailServerSkill(agent, config)


# CLI commands for standalone use
async def cli_main():
    """CLI entry point for email server management."""
    import argparse

    parser = argparse.ArgumentParser(
        description="Familiar Email Server",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Start the email server
  python -m familiar.skills.email_server start

  # Show required DNS records
  python -m familiar.skills.email_server dns-setup

  # Verify DNS configuration
  python -m familiar.skills.email_server verify-dns

  # Create a new user
  python -m familiar.skills.email_server add-user user@example.com

  # Send a test email
  python -m familiar.skills.email_server test-send to@example.com
""",
    )

    subparsers = parser.add_subparsers(dest="command", help="Command to run")

    # Start command
    start_parser = subparsers.add_parser("start", help="Start the email server")
    start_parser.add_argument("--domain", required=True, help="Email domain")
    start_parser.add_argument("--config", help="Config file path")

    # DNS setup command
    dns_parser = subparsers.add_parser("dns-setup", help="Show DNS setup guide")
    dns_parser.add_argument("--domain", required=True, help="Email domain")

    # Verify DNS command
    verify_parser = subparsers.add_parser("verify-dns", help="Verify DNS records")
    verify_parser.add_argument("--domain", required=True, help="Email domain")

    # Add user command
    user_parser = subparsers.add_parser("add-user", help="Add email user")
    user_parser.add_argument("email", help="Email address")
    user_parser.add_argument("--password", help="Password (prompted if not provided)")

    # Test send command
    test_parser = subparsers.add_parser("test-send", help="Send test email")
    test_parser.add_argument("to", help="Recipient address")
    test_parser.add_argument("--from", dest="from_addr", help="Sender address")

    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        return

    if args.command == "start":
        await cmd_start(args)
    elif args.command == "dns-setup":
        await cmd_dns_setup(args)
    elif args.command == "verify-dns":
        await cmd_verify_dns(args)
    elif args.command == "add-user":
        await cmd_add_user(args)
    elif args.command == "test-send":
        await cmd_test_send(args)


async def cmd_start(args):
    """Start the email server."""
    config = {
        "email_server": {
            "domain": args.domain,
        }
    }

    skill = EmailServerSkill(None, config)
    await skill.start()

    print(f"Email server started for {args.domain}")
    print(f"SMTP: port {skill.config.smtp_port}")
    print(f"IMAP: port {skill.config.imap_port}")
    print("\nPress Ctrl+C to stop")

    try:
        while True:
            await asyncio.sleep(1)
    except KeyboardInterrupt:
        await skill.stop()
        print("\nEmail server stopped")


async def cmd_dns_setup(args):
    """Show DNS setup guide."""
    from pathlib import Path

    from .dkim import DKIMManager
    from .dns_manager import DNSManager

    # Initialize DKIM to get the public key
    dkim_path = Path.home() / ".familiar" / "mail" / "dkim"
    dkim = DKIMManager(args.domain, dkim_path)
    await dkim.initialize()

    dns = DNSManager(args.domain, f"mail.{args.domain}", dkim)
    guide = await dns.get_setup_guide()

    print(guide)


async def cmd_verify_dns(args):
    """Verify DNS configuration."""
    from .dns_manager import DNSManager

    dns = DNSManager(args.domain, f"mail.{args.domain}")
    results = await dns.verify_records()

    print(f"\n=== DNS Verification for {args.domain} ===\n")

    for record_type, status in results.get("records", {}).items():
        icon = "✓" if status.get("valid") else "✗"
        print(f"{icon} {record_type.upper()}: {status.get('message')}")
        if status.get("found"):
            print(f"    Found: {status['found']}")
        if status.get("expected"):
            print(f"    Expected: {status['expected']}")

    print()
    if results["overall"]:
        print("✓ All DNS records are correctly configured!")
    else:
        print("✗ Some DNS records need attention.")
        print("  Run: python -m familiar.skills.email_server dns-setup --domain", args.domain)


async def cmd_add_user(args):
    """Add an email user."""
    import getpass
    from pathlib import Path

    from .storage import MailStorage

    password = args.password
    if not password:
        password = getpass.getpass(f"Password for {args.email}: ")
        confirm = getpass.getpass("Confirm password: ")
        if password != confirm:
            print("Passwords don't match")
            return

    storage_path = Path.home() / ".familiar" / "mail"
    storage = MailStorage(storage_path, None)
    await storage.initialize()

    import hashlib
    import os as _os

    salt = _os.urandom(16).hex()
    password_hash = (
        salt + ":" + hashlib.pbkdf2_hmac("sha256", password.encode(), salt.encode(), 100_000).hex()
    )

    if await storage.create_mailbox(args.email, password_hash):
        print(f"✓ Created mailbox for {args.email}")
    else:
        print(f"✗ Mailbox {args.email} already exists")


async def cmd_test_send(args):
    """Send a test email."""
    from dataclasses import dataclass

    from .smtp_client import SMTPClient

    @dataclass
    class TestConfig:
        domain: str = "localhost"
        relay_enabled: bool = False
        relay_host: str = None
        relay_port: int = 587
        relay_username: str = None
        relay_password: str = None

    config = TestConfig()
    from_addr = args.from_addr or f"test@{config.domain}"

    client = SMTPClient(config, None)

    try:
        result = await client.send(
            from_address=from_addr,
            to=args.to,
            subject="Familiar Email Server Test",
            body="This is a test email from Familiar Email Server.\n\nIf you received this, your email server is working!",
        )

        print(f"✓ Test email sent to {args.to}")
        print(f"  Message-ID: {result['message_id']}")
    except Exception as e:
        print(f"✗ Failed to send: {e}")


if __name__ == "__main__":
    import asyncio

    asyncio.run(cli_main())


# ── LLM-callable tool wrappers ──────────────────────────────────
# The EmailServerSkill methods are async on a class instance.
# These wrappers provide the sync, dict-based interface the
# skill loader expects.

import asyncio as _asyncio  # noqa: E402
import logging as _logger  # noqa: E402

_log = _logger.getLogger(__name__)


def _run_async(coro):
    """Run async code from sync context."""
    try:
        loop = _asyncio.get_event_loop()
        if loop.is_running():
            import concurrent.futures

            with concurrent.futures.ThreadPoolExecutor() as pool:
                return pool.submit(_asyncio.run, coro).result(timeout=30)
        return loop.run_until_complete(coro)
    except RuntimeError:
        return _asyncio.run(coro)


# Lazy server instance — only created if tools are actually called
_server_instance = None


def _get_server():
    # (read-only access to _server_instance)
    if _server_instance is None:
        _log.warning("Email server not initialized. Use /connect email-server to configure.")
        return None
    return _server_instance


def email_server_status(data: dict) -> str:
    """Get email server status and statistics."""
    server = _get_server()
    if not server:
        return "Email server not configured. Set up with /connect email-server."
    try:
        stats = _run_async(server.get_stats())
        lines = [f"📧 Email Server: {stats.get('domain', 'unknown')}"]
        lines.append(f"  Running: {'✓' if stats.get('running') else '✗'}")
        lines.append(f"  Users: {', '.join(stats.get('users', [])) or 'none'}")
        ports = stats.get("ports", {})
        lines.append(f"  SMTP: {ports.get('smtp', '?')}  IMAP: {ports.get('imap', '?')}")
        lines.append(
            f"  Relay: {'✓' if stats.get('relay_enabled') else '✗'}  "
            f"Encryption: {'✓' if stats.get('encryption_enabled') else '✗'}  "
            f"Spam filter: {'✓' if stats.get('spam_filter_enabled') else '✗'}"
        )
        return "\n".join(lines)
    except Exception as e:
        return f"Error: {e}. Check email server config with /connect email-server."


def email_server_send(data: dict) -> str:
    """Send an email via the self-hosted email server."""
    server = _get_server()
    if not server:
        return "Email server not configured."
    to = data.get("to", "").strip()
    subject = data.get("subject", "").strip()
    body = data.get("body", "").strip()
    if not to or not subject or not body:
        return "Please provide to, subject, and body."
    try:
        result = _run_async(server.send(to=to, subject=subject, body=body))
        if result.get("success"):
            return f"✓ Email sent to {to} (ID: {result.get('message_id', '?')})"
        return f"✗ Failed: {result.get('error', 'unknown')}"
    except Exception as e:
        return f"Error: {e}. Check email server config with /connect email-server."


def email_server_inbox(data: dict) -> str:
    """Check the self-hosted email server inbox."""
    server = _get_server()
    if not server:
        return "Email server not configured."
    address = data.get("address", "").strip()
    folder = data.get("folder", "INBOX")
    limit = int(data.get("limit", 20))
    if not address:
        return "Please provide an email address."
    try:
        messages = _run_async(server.get_mailbox(address, folder, limit))
        if not messages:
            return f"No messages in {folder} for {address}."
        lines = [f"📬 {address} — {folder} ({len(messages)} messages):"]
        for msg in messages[:20]:
            read = "  " if msg.get("read") else "● "
            lines.append(f"  {read}{msg['sender']}: {msg['subject']} ({msg['date'][:10]})")
        return "\n".join(lines)
    except Exception as e:
        return f"Error: {e}. Check email server config with /connect email-server."


def email_server_read(data: dict) -> str:
    """Read a specific email from the self-hosted server."""
    server = _get_server()
    if not server:
        return "Email server not configured."
    address = data.get("address", "").strip()
    message_id = data.get("message_id", "").strip()
    if not address or not message_id:
        return "Please provide address and message_id."
    try:
        msg = _run_async(server.get_message(address, message_id))
        if not msg:
            return "Message not found."
        lines = [
            f"From: {msg['sender']}",
            f"To: {msg.get('to', '?')}",
            f"Subject: {msg['subject']}",
            f"Date: {msg['date']}",
            "",
            msg.get("body", "(no body)"),
        ]
        if msg.get("attachments"):
            lines.append(
                f"\n📎 Attachments: {', '.join(a['filename'] for a in msg['attachments'])}"
            )
        return "\n".join(lines)
    except Exception as e:
        return f"Error: {e}. Check email server config with /connect email-server."


def email_server_search(data: dict) -> str:
    """Search emails on the self-hosted server."""
    server = _get_server()
    if not server:
        return "Email server not configured."
    address = data.get("address", "").strip()
    query = data.get("query", "").strip()
    if not address or not query:
        return "Please provide address and query."
    try:
        results = _run_async(server.search(address, query))
        if not results:
            return f"No results for '{query}'."
        lines = [f"🔍 Search '{query}' — {len(results)} results:"]
        for msg in results[:20]:
            lines.append(f"  {msg['sender']}: {msg['subject']} ({msg['date'][:10]})")
        return "\n".join(lines)
    except Exception as e:
        return f"Error: {e}. Check email server config with /connect email-server."


def email_server_users(data: dict) -> str:
    """List email server users."""
    server = _get_server()
    if not server:
        return "Email server not configured."
    try:
        users = _run_async(server.list_users())
        if not users:
            return "No email users configured."
        return "📧 Email users:\n" + "\n".join(f"  • {u}" for u in users)
    except Exception as e:
        return f"Error: {e}. Check email server config with /connect email-server."


TOOLS = [
    {
        "name": "email_server_status",
        "description": (
            "Get self-hosted email server status including ports, users, and configuration. "
            "Use this to verify the server is running and check which accounts are active. "
            "Requires /connect email-server setup. "
            "Returns running state, SMTP/IMAP ports, relay and encryption status."
        ),
        "input_schema": {"type": "object", "properties": {}, "required": []},
        "handler": email_server_status,
        "category": "email_server",
    },
    {
        "name": "email_server_send",
        "description": (
            "Send an email through the self-hosted Mailcow/Postfix email server. "
            "Use this for server-level admin sending, NOT for personal email — use send_email instead. "
            "Requires to, subject, and body parameters. "
            "Returns a success confirmation with message ID or an error with recovery hints."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "to": {"type": "string", "description": "Recipient email address"},
                "subject": {"type": "string", "description": "Email subject"},
                "body": {"type": "string", "description": "Email body text"},
            },
            "required": ["to", "subject", "body"],
        },
        "handler": email_server_send,
        "category": "email_server",
    },
    {
        "name": "email_server_inbox",
        "description": (
            "Check a mailbox on the self-hosted Mailcow/Postfix email server. "
            "Use this for admin-level mailbox inspection, NOT for personal inbox — use check_inbox instead. "
            "Supports folder selection (INBOX, Sent, Drafts, Trash, Spam) and message limits. "
            "Returns up to 20 messages with sender, subject, and date."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "address": {"type": "string", "description": "Email address to check"},
                "folder": {
                    "type": "string",
                    "description": "Folder name (INBOX, Sent, Drafts, Trash, Spam)",
                },
                "limit": {"type": "integer", "description": "Max messages to return (default 20)"},
            },
            "required": ["address"],
        },
        "handler": email_server_inbox,
        "category": "email_server",
    },
    {
        "name": "email_server_read",
        "description": (
            "Read a specific email by message ID on the self-hosted Mailcow/Postfix server. "
            "Use this for admin-level message inspection, NOT for personal email — use read_email instead. "
            "Requires address and message_id from a prior inbox listing. "
            "Returns full message headers, body text, and attachment filenames."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "address": {"type": "string", "description": "Email address"},
                "message_id": {"type": "string", "description": "Message ID from inbox listing"},
            },
            "required": ["address", "message_id"],
        },
        "handler": email_server_read,
        "category": "email_server",
    },
    {
        "name": "email_server_search",
        "description": (
            "Search mailboxes on the self-hosted Mailcow/Postfix email server by query string. "
            "Use this for admin-level mailbox searching, NOT for personal inbox — use search_emails instead. "
            "Requires address and query parameters. "
            "Returns up to 20 matching messages with sender, subject, and date."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "address": {"type": "string", "description": "Email address to search"},
                "query": {"type": "string", "description": "Search query"},
            },
            "required": ["address", "query"],
        },
        "handler": email_server_search,
        "category": "email_server",
    },
    {
        "name": "email_server_users",
        "description": (
            "List all user accounts on the self-hosted email server. "
            "Use this to see which mailboxes exist before checking or sending mail. "
            "Requires /connect email-server setup. "
            "Returns a bullet list of email addresses or a message if none are configured."
        ),
        "input_schema": {"type": "object", "properties": {}, "required": []},
        "handler": email_server_users,
        "category": "email_server",
    },
]
