import base64
import logging
import os
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from google.auth.transport.requests import Request as GoogleRequest
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import Flow
from googleapiclient.discovery import build
from pathlib import Path
from typing import Optional

from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import HTMLResponse, RedirectResponse

from app.config import Settings, get_settings

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/email", tags=["email"])

# Narrowest possible scope: send only, cannot read mail.
GMAIL_SEND_SCOPE = ["https://www.googleapis.com/auth/gmail.send"]

# Public entrypoint
def send_email(to: str, subject: str, body_html: str) -> bool:
	"""
	Send an HTML email. Returns True on success, False on any failure
	or if no backend is configured.
	"""
	cfg = get_settings()
	backend = (cfg.email_backend or "").strip().lower()

	if backend == "gmail_api":
		return _send_via_gmail_oauth_user(to, subject, body_html)

	if backend == "smtp":
		return _send_via_smtp(to, subject, body_html)

	# No backend configured: stay silent so demo still runs.
	logger.debug("EMAIL_BACKEND not set, skipping email to %s", to)
	return False


# Shared MIME builder
def _build_message(sender: str, to: str, subject: str, body_html: str) -> MIMEMultipart:
	msg = MIMEMultipart("alternative")
	msg["From"] = sender
	msg["To"] = to
	msg["Subject"] = subject
	msg.attach(MIMEText(body_html, "html"))
	return msg


# Backend 1: Gmail API via OAuth user flow
def _send_via_gmail_oauth_user(to: str, subject: str, body_html: str) -> bool:
	"""
	Uses the Gmail API with a user's OAuth credentials.
	Good for: personal Gmail accounts, dev / demo, single-sender setups.

	Required .env:
	  EMAIL_BACKEND=gmail_api
	  GMAIL_CREDENTIALS_FILE=./secrets/credentials.json
	  GMAIL_TOKEN_FILE=./secrets/token.json
	  GMAIL_SENDER=you@gmail.com
	"""
	cfg = get_settings()

	creds = get_gmail_creds(cfg)
	if creds is None:
		logger.warning("No authorized Gmail account found.")
		return False

	sender = cfg.gmail_sender or ""
	if not sender:
		logger.error("GMAIL_SENDER is empty, cannot send email")
		return False

	try:
		service = build("gmail", "v1", credentials=creds, cache_discovery=False)
		msg = _build_message(sender, to, subject, body_html)
		raw = base64.urlsafe_b64encode(msg.as_bytes()).decode("ascii")
		service.users().messages().send(userId="me", body={"raw": raw}).execute()
		logger.info("Gmail API sent mail to %s, subject=%s", to, subject)
		return True
	except Exception as exc:
		logger.error("Gmail API send failed for %s: %s", to, exc)
		return False


def get_gmail_creds(cfg: Settings) -> Optional[Credentials]:
	creds_path = Path(cfg.gmail_credentials_file)
	token_path = Path(cfg.gmail_token_file)
	if not creds_path.exists():
		logger.error("Gmail credentials file missing.")
		return None
	creds = None
	if token_path.exists():
		try:
			creds = Credentials.from_authorized_user_file(str(token_path), GMAIL_SEND_SCOPE)
		except Exception:
			creds = None
	if creds and creds.valid:
		return creds
	if creds and creds.expired and creds.refresh_token:
		try:
			creds.refresh(GoogleRequest())
			token_path.write_text(creds.to_json())
			return creds
		except Exception as exc:
			logger.error("Refresh failed: %s", exc)
			return None
	return None

# Backend 2: SMTP
def _send_via_smtp(to: str, subject: str, body_html: str) -> bool:
	cfg = get_settings()
	if not cfg.smtp_host or not cfg.smtp_user:
		logger.debug("SMTP not configured, skipping email to %s", to)
		return False

	try:
		sender = cfg.smtp_from or cfg.smtp_user
		msg = _build_message(sender, to, subject, body_html)
		with smtplib.SMTP(cfg.smtp_host, cfg.smtp_port) as server:
			server.ehlo()
			server.starttls()
			server.login(cfg.smtp_user, cfg.smtp_password)
			server.sendmail(sender, [to], msg.as_string())
		logger.info("SMTP sent mail to %s, subject=%s", to, subject)
		return True
	except Exception as exc:
		logger.error("SMTP send failed for %s: %s", to, exc)
		return False

@router.get("/gmail-login")
async def gmail_login():
	cfg = get_settings()
	redirect_uri = cfg.base_url + cfg.root_url + router.prefix + "/gmail-callback"
	flow = Flow.from_client_secrets_file(cfg.gmail_credentials_file, scopes=GMAIL_SEND_SCOPE, redirect_uri=redirect_uri)
	authorization_url, state = flow.authorization_url(access_type="offline", include_granted_scopes="true", prompt="consent")
	Path(cfg.gmail_tmp_state).write_text(state + "\n" + flow.code_verifier)
	return RedirectResponse(url=authorization_url)

@router.get("/gmail-callback")
async def gmail_callback(request: Request):
	cfg = get_settings()
	txt = Path(cfg.gmail_tmp_state).read_text()
	if txt is None:
		raise HTTPException(400, "Missing OAuth state")
	tmp_state = str.splitlines(txt)
	redirect_uri = cfg.base_url + cfg.root_url + router.prefix + "/gmail-callback"
	flow = Flow.from_client_secrets_file(cfg.gmail_credentials_file, scopes=GMAIL_SEND_SCOPE, state=tmp_state[0], redirect_uri=redirect_uri)
	flow.code_verifier = tmp_state[1]
	flow.fetch_token(authorization_response=str(request.url))
	creds = flow.credentials
	token_path = Path(cfg.gmail_token_file)
	token_path.parent.mkdir(parents=True, exist_ok=True)
	token_path.write_text(creds.to_json())
	os.remove(cfg.gmail_tmp_state)
	return HTMLResponse(
		"<h2>Gmail authorization complete.</h2>"
	)