import os
from datetime import datetime, timedelta, timezone
from pathlib import Path
from urllib.parse import quote
from uuid import uuid4

from fastapi import APIRouter, Cookie, Depends, File, HTTPException, Query, UploadFile
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
from pydantic import BaseModel, Field
from jose import jwt
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.models import SystemConfig, Tenant, User
from app.core.security import ALGORITHM, JWT_SECRET, PLATFORM_TENANT_CODE, decode_jwt_token, require_admin, require_permission
from app.services.email_oauth import (
    PROVIDERS,
    build_authorization_url,
    email_from_id_token,
    encrypt_refresh_token,
    exchange_code,
    provider_configured,
)
from app.services.email_service import send_email
from app.services.invoice_pdf import (
    AVAILABLE_TEMPLATE_VARIABLES,
    DEFAULT_INVOICE_TEMPLATE,
    DEFAULT_INVOICE_EMAIL_BODY_TEMPLATE,
    DEFAULT_INVOICE_EMAIL_SUBJECT_TEMPLATE,
    DEFAULT_PAYMENT_METHODS,
)
from app.core.config import get_ai_config as runtime_ai_config

router = APIRouter(prefix="/api/config", tags=["config"])
BRANDING_UPLOAD_ROOT = Path(__file__).resolve().parents[2] / "storage" / "branding"
BRANDING_UPLOAD_ROOT.mkdir(parents=True, exist_ok=True)


# ===== Helpers =====

def _get_config_row(db: Session, tenant_id: int, key_name: str) -> SystemConfig | None:
    return db.execute(
        select(SystemConfig).where(
            SystemConfig.tenant_id == tenant_id,
            SystemConfig.key_name == key_name,
        )
    ).scalar_one_or_none()


def _get_secret_masked(db: Session, tenant_id: int, key_name: str) -> str | None:
    row = _get_config_row(db, tenant_id, key_name)
    if not row:
        return None
    if row.is_secret and row.key_value:
        return "******"
    return row.key_value


def _upsert_secret(
    db: Session,
    tenant_id: int,
    key_name: str,
    value: str,
    is_secret: bool = False,
    *,
    commit: bool = True,
):
    row = _get_config_row(db, tenant_id, key_name)
    if row:
        row.key_value = value
        row.is_secret = is_secret
    else:
        row = SystemConfig(tenant_id=tenant_id, key_name=key_name, key_value=value, is_secret=is_secret)
        db.add(row)
    if commit:
        db.commit()


def _get_config_value(db: Session, tenant_id: int, key_name: str, default: str = "") -> str:
    row = _get_config_row(db, tenant_id, key_name)
    if row and row.key_value is not None:
        return row.key_value
    return default


def get_default_currency(db: Session, tenant_id: int) -> str:
    """The tenant-scoped default currency, used whenever a new record omits one."""
    return _get_config_value(db, tenant_id, "default_currency", SYSTEM_DEFAULTS["default_currency"])


# ===== System Basics =====

SYSTEM_DEFAULTS = {
    "system_name": "ERP Control",
    "system_subtitle": "IT service operations",
    "company_name": "IT Service Company",
    "company_tax_number": "",
    "default_currency": "NZD",
    "timezone": "Pacific/Auckland",
    "date_format": "YYYY-MM-DD",
}
SYSTEM_LOGO_PATH_KEY = "system_logo_path"
SYSTEM_LOGO_FILENAME_KEY = "system_logo_filename"
SYSTEM_LOGO_MIME_KEY = "system_logo_mime"
ALLOWED_LOGO_MIME_TYPES = {
    "image/png": ".png",
    "image/jpeg": ".jpg",
    "image/webp": ".webp",
    "image/svg+xml": ".svg",
}


class SystemSettingsResponse(BaseModel):
    system_name: str
    system_subtitle: str
    company_name: str
    company_tax_number: str
    default_currency: str
    timezone: str
    date_format: str
    logo_url: str | None = None


class SystemSettingsUpdate(BaseModel):
    system_name: str = Field(min_length=1, max_length=80)
    system_subtitle: str = Field(max_length=120)
    company_name: str = Field(max_length=120)
    company_tax_number: str = Field(max_length=80)
    default_currency: str = Field(min_length=1, max_length=10)
    timezone: str = Field(max_length=80)
    date_format: str = Field(max_length=40)


def _tenant_by_company_code(db: Session, company_code: str | None) -> Tenant | None:
    if not company_code:
        return None
    return db.execute(
        select(Tenant).where(Tenant.company_code == company_code.strip().lower())
    ).scalar_one_or_none()


def _get_system_settings(
    db: Session,
    tenant_id: int | None = None,
    company_code: str | None = None,
) -> SystemSettingsResponse:
    if tenant_id is None:
        tenant = _tenant_by_company_code(db, company_code)
        if tenant:
            tenant_id = tenant.id
            company_code = tenant.company_code
    elif not company_code:
        tenant = db.get(Tenant, tenant_id)
        company_code = tenant.company_code if tenant else None
    if tenant_id is None:
        return SystemSettingsResponse(**SYSTEM_DEFAULTS)

    logo_url = None
    logo_path = _get_config_value(db, tenant_id, SYSTEM_LOGO_PATH_KEY)
    if logo_path:
        path = Path(logo_path)
        if path.exists():
            code = f"&company_code={quote(company_code)}" if company_code else ""
            logo_url = f"/api/config/system/logo?v={path.stat().st_mtime_ns}{code}"

    return SystemSettingsResponse(
        logo_url=logo_url,
        **{
            key: _get_config_value(db, tenant_id, key, default)
            for key, default in SYSTEM_DEFAULTS.items()
        },
    )


@router.get("/system", response_model=SystemSettingsResponse)
def get_system_settings(
    company_code: str | None = Query(None),
    db: Session = Depends(get_db),
):
    return _get_system_settings(db, tenant_id=None, company_code=company_code)


@router.put("/system", response_model=SystemSettingsResponse)
def update_system_settings(
    data: SystemSettingsUpdate,
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    for key, value in data.model_dump().items():
        _upsert_secret(db, tid, key, value.strip(), is_secret=False, commit=False)
    db.commit()
    return _get_system_settings(db, tid)


def _delete_logo_file(path_str: str | None) -> None:
    if not path_str:
        return
    path = Path(path_str)
    if path.exists():
        path.unlink()


def _clear_system_logo_config(db: Session, tenant_id: int) -> None:
    old_path = _get_config_value(db, tenant_id, SYSTEM_LOGO_PATH_KEY)
    for key_name in (SYSTEM_LOGO_PATH_KEY, SYSTEM_LOGO_FILENAME_KEY, SYSTEM_LOGO_MIME_KEY):
        row = _get_config_row(db, tenant_id, key_name)
        if row:
            db.delete(row)
    db.commit()
    _delete_logo_file(old_path)


@router.post("/system/logo", response_model=SystemSettingsResponse)
async def upload_system_logo(
    file: UploadFile = File(...),
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    content_type = (file.content_type or "").lower()
    suffix = ALLOWED_LOGO_MIME_TYPES.get(content_type)
    if not suffix:
        raise HTTPException(
            status_code=400,
            detail="Unsupported logo format. Please upload PNG, JPG, WEBP, or SVG.",
        )

    file_bytes = await file.read()
    if not file_bytes:
        raise HTTPException(status_code=400, detail="Logo file is empty.")
    if len(file_bytes) > 2 * 1024 * 1024:
        raise HTTPException(status_code=400, detail="Logo file must be 2MB or smaller.")

    tid = current_user.tenant_id
    old_path = _get_config_value(db, tid, SYSTEM_LOGO_PATH_KEY)

    target = BRANDING_UPLOAD_ROOT / f"system-logo-{uuid4().hex}{suffix}"
    target.write_bytes(file_bytes)

    try:
        _upsert_secret(db, tid, SYSTEM_LOGO_PATH_KEY, str(target), is_secret=False, commit=False)
        _upsert_secret(db, tid, SYSTEM_LOGO_FILENAME_KEY, file.filename or target.name, is_secret=False, commit=False)
        _upsert_secret(db, tid, SYSTEM_LOGO_MIME_KEY, content_type, is_secret=False, commit=False)
        db.commit()
    except Exception:
        db.rollback()
        _delete_logo_file(str(target))
        raise

    _delete_logo_file(old_path)
    return _get_system_settings(db, tid)


@router.delete("/system/logo", response_model=SystemSettingsResponse)
def delete_system_logo(
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    _clear_system_logo_config(db, tid)
    return _get_system_settings(db, tid)


@router.get("/system/logo")
def get_system_logo(
    company_code: str = Query(...),
    db: Session = Depends(get_db),
):
    tenant = _tenant_by_company_code(db, company_code)
    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found")
    row = db.execute(
        select(SystemConfig).where(
            SystemConfig.tenant_id == tenant.id,
            SystemConfig.key_name == SYSTEM_LOGO_PATH_KEY,
        )
    ).scalar_one_or_none()
    logo_path = row.key_value if row and row.key_value else None
    if not logo_path:
        raise HTTPException(status_code=404, detail="System logo not configured")

    path = Path(logo_path)
    if not path.exists():
        raise HTTPException(status_code=404, detail="System logo file not found")

    tid = row.tenant_id
    media_type = _get_config_value(db, tid, SYSTEM_LOGO_MIME_KEY, "application/octet-stream")
    filename = _get_config_value(db, tid, SYSTEM_LOGO_FILENAME_KEY, path.name)
    return FileResponse(path=path, media_type=media_type, filename=filename)


# ===== SMTP =====

class SMTPConfigResponse(BaseModel):
    model_config = {"from_attributes": True}

    smtp_host: str | None = None
    smtp_port: int | None = None
    smtp_user: str | None = None
    smtp_password: str | None = None
    smtp_from_name: str | None = None
    smtp_from_email: str | None = None
    smtp_use_tls: bool | None = None


class SMTPConfigUpdate(BaseModel):
    smtp_host: str | None = None
    smtp_port: int | None = None
    smtp_user: str | None = None
    smtp_password: str | None = None
    smtp_from_name: str | None = None
    smtp_from_email: str | None = None
    smtp_use_tls: bool | None = None


class SMTPTestRequest(BaseModel):
    to_email: str


smtp_keys = ["smtp_host", "smtp_port", "smtp_user", "smtp_password",
             "smtp_from_name", "smtp_from_email", "smtp_use_tls"]

EMAIL_TRANSPORT_KEY = "email_transport"
EMAIL_OAUTH_PROVIDER_KEY = "email_oauth_provider"
EMAIL_OAUTH_EMAIL_KEY = "email_oauth_email"
EMAIL_OAUTH_REFRESH_TOKEN_KEY = "email_oauth_refresh_token"
EMAIL_OAUTH_STATE_COOKIE = "email_oauth_state"


@router.get("/smtp", response_model=SMTPConfigResponse)
def get_smtp_config(
    current_user: User = Depends(require_permission("settings.read")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    result = {}
    for key in smtp_keys:
        if key == "smtp_use_tls":
            raw = _get_secret_masked(db, tid, "smtp_use_tls")
            result["smtp_use_tls"] = raw.lower() == "true" if raw else True
        elif key == "smtp_port":
            raw = _get_secret_masked(db, tid, key)
            result["smtp_port"] = int(raw) if raw else None
        else:
            result[key] = _get_secret_masked(db, tid, key)
    return SMTPConfigResponse(**result)


@router.put("/smtp", response_model=SMTPConfigResponse)
def update_smtp_config(
    data: SMTPConfigUpdate,
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    update_map = data.model_dump(exclude_unset=True)
    for key, value in update_map.items():
        is_secret = key == "smtp_password"
        if key == "smtp_use_tls":
            value = "true" if value else "false"
        if key == "smtp_port" and value is not None:
            value = str(value)
        _upsert_secret(
            db,
            tid,
            key,
            str(value) if value is not None else "",
            is_secret=is_secret,
            commit=False,
        )
    db.commit()
    return get_smtp_config(current_user=current_user, db=db)


@router.post("/smtp/test")
async def test_smtp_config(
    data: SMTPTestRequest,
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    result = await send_email(
        to_email=data.to_email,
        subject="ERP SMTP Test",
        body="This is a test email from ERP.",
        db=db,
        tenant_id=current_user.tenant_id,
    )
    if not result["success"]:
        raise HTTPException(status_code=400, detail=result["error"])
    return {"success": True, "message": "Test email sent"}


# ===== Email transport =====

class EmailTransportResponse(BaseModel):
    transport: str = "smtp"
    connected_email: str | None = None
    connected_provider: str | None = None
    providers: dict[str, bool]


class EmailTransportUpdate(BaseModel):
    transport: str


def _email_transport_response(db: Session, tenant_id: int) -> EmailTransportResponse:
    transport = _get_config_value(db, tenant_id, EMAIL_TRANSPORT_KEY, "")
    if transport not in {"smtp", *PROVIDERS}:
        transport = "google" if provider_configured("google") else "smtp"
    provider = _get_config_value(db, tenant_id, EMAIL_OAUTH_PROVIDER_KEY) or None
    email = _get_config_value(db, tenant_id, EMAIL_OAUTH_EMAIL_KEY) or None
    has_token = _get_config_row(db, tenant_id, EMAIL_OAUTH_REFRESH_TOKEN_KEY)
    return EmailTransportResponse(
        transport=transport,
        connected_email=email if has_token and has_token.encrypted_value else None,
        connected_provider=provider if has_token and has_token.encrypted_value else None,
        providers={name: provider_configured(name) for name in PROVIDERS},
    )


def _email_oauth_state(provider: str, tenant_id: int) -> str:
    return jwt.encode(
        {
            "type": "email_oauth",
            "provider": provider,
            "tenant_id": tenant_id,
            "exp": datetime.now(timezone.utc) + timedelta(minutes=10),
        },
        JWT_SECRET,
        algorithm=ALGORITHM,
    )


def _oauth_return_url(status: str) -> str:
    return f"{os.getenv('PUBLIC_APP_URL', '').rstrip('/')}/invoice-settings?email_oauth={quote(status)}"


@router.get("/email-transport", response_model=EmailTransportResponse)
def get_email_transport(
    current_user: User = Depends(require_permission("settings.read")),
    db: Session = Depends(get_db),
):
    return _email_transport_response(db, current_user.tenant_id)


@router.put("/email-transport", response_model=EmailTransportResponse)
def update_email_transport(
    data: EmailTransportUpdate,
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    if data.transport not in {"smtp", *PROVIDERS}:
        raise HTTPException(status_code=400, detail="Unsupported email transport")
    if data.transport in PROVIDERS and not provider_configured(data.transport):
        raise HTTPException(status_code=400, detail="Email provider is not configured by the platform")
    _upsert_secret(db, current_user.tenant_id, EMAIL_TRANSPORT_KEY, data.transport, commit=False)
    db.commit()
    return _email_transport_response(db, current_user.tenant_id)


@router.post("/email-oauth/{provider}/start")
def start_email_oauth(
    provider: str,
    current_user: User = Depends(require_permission("settings.write")),
):
    if provider not in PROVIDERS or not provider_configured(provider):
        raise HTTPException(status_code=400, detail="Email provider is not configured by the platform")
    state = _email_oauth_state(provider, current_user.tenant_id)
    response = JSONResponse({"authorization_url": build_authorization_url(provider, state)})
    response.set_cookie(EMAIL_OAUTH_STATE_COOKIE, state, httponly=True, secure=True, samesite="lax", max_age=600)
    return response


@router.get("/email-oauth/{provider}/callback")
async def complete_email_oauth(
    provider: str,
    code: str | None = None,
    state: str | None = None,
    error: str | None = None,
    email_oauth_state: str | None = Cookie(None),
    db: Session = Depends(get_db),
):
    if error or not code or not state or state != email_oauth_state or provider not in PROVIDERS:
        return RedirectResponse(_oauth_return_url("failed"), status_code=303)
    try:
        payload = decode_jwt_token(state)
        if payload.get("type") != "email_oauth" or payload.get("provider") != provider or not payload.get("tenant_id"):
            raise ValueError("Invalid OAuth state")
        token_data = await exchange_code(provider, code)
        refresh_token = token_data.get("refresh_token")
        sender_email = email_from_id_token(token_data.get("id_token", ""))
        if not refresh_token or not sender_email:
            raise ValueError("OAuth provider did not return a mailbox authorization")
        tenant_id = int(payload["tenant_id"])
        _upsert_secret(db, tenant_id, EMAIL_OAUTH_PROVIDER_KEY, provider, commit=False)
        _upsert_secret(db, tenant_id, EMAIL_OAUTH_EMAIL_KEY, sender_email, commit=False)
        _upsert_secret(db, tenant_id, EMAIL_TRANSPORT_KEY, provider, commit=False)
        row = _get_config_row(db, tenant_id, EMAIL_OAUTH_REFRESH_TOKEN_KEY)
        if row:
            row.key_value = None
            row.encrypted_value = encrypt_refresh_token(refresh_token)
            row.is_secret = True
        else:
            db.add(SystemConfig(tenant_id=tenant_id, key_name=EMAIL_OAUTH_REFRESH_TOKEN_KEY, encrypted_value=encrypt_refresh_token(refresh_token), is_secret=True))
        db.commit()
        response = RedirectResponse(_oauth_return_url("connected"), status_code=303)
    except Exception:
        db.rollback()
        response = RedirectResponse(_oauth_return_url("failed"), status_code=303)
    response.delete_cookie(EMAIL_OAUTH_STATE_COOKIE)
    return response


@router.delete("/email-oauth/{provider}", response_model=EmailTransportResponse)
def disconnect_email_oauth(
    provider: str,
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    if provider not in PROVIDERS:
        raise HTTPException(status_code=400, detail="Unsupported email provider")
    if _get_config_value(db, current_user.tenant_id, EMAIL_OAUTH_PROVIDER_KEY) == provider:
        for key_name in (EMAIL_OAUTH_PROVIDER_KEY, EMAIL_OAUTH_EMAIL_KEY, EMAIL_OAUTH_REFRESH_TOKEN_KEY):
            row = _get_config_row(db, current_user.tenant_id, key_name)
            if row:
                db.delete(row)
        _upsert_secret(db, current_user.tenant_id, EMAIL_TRANSPORT_KEY, "smtp", commit=False)
        db.commit()
    return _email_transport_response(db, current_user.tenant_id)


# ===== Telegram =====

class TelegramConfigResponse(BaseModel):
    bot_token: str | None
    chat_id: str | None = None


class TelegramConfigUpdate(BaseModel):
    bot_token: str | None = None
    chat_id: str | None = None


def _require_custom_telegram_access(current_user: User, db: Session) -> None:
    tenant = db.get(Tenant, current_user.tenant_id)
    if tenant and tenant.company_code != PLATFORM_TENANT_CODE and tenant.telegram_use_platform_bot:
        raise HTTPException(status_code=403, detail="Your tenant uses the platform Telegram Bot. Ask the platform administrator to allow a custom Bot.")


@router.get("/telegram", response_model=TelegramConfigResponse)
def get_telegram_config(
    current_user: User = Depends(require_admin),
    db: Session = Depends(get_db),
):
    _require_custom_telegram_access(current_user, db)
    tid = current_user.tenant_id
    return TelegramConfigResponse(
        bot_token=_get_secret_masked(db, tid, "telegram_bot_token"),
        chat_id=_get_config_value(db, tid, "telegram_chat_id"),
    )


@router.put("/telegram", response_model=TelegramConfigResponse)
def update_telegram_config(
    data: TelegramConfigUpdate,
    current_user: User = Depends(require_admin),
    db: Session = Depends(get_db),
):
    _require_custom_telegram_access(current_user, db)
    tid = current_user.tenant_id
    if data.bot_token is not None:
        _upsert_secret(db, tid, "telegram_bot_token", data.bot_token, is_secret=True, commit=False)
    if data.chat_id is not None:
        chat_id = data.chat_id.strip()
        existing = db.execute(
            select(SystemConfig).where(
                SystemConfig.key_name == "telegram_chat_id",
                SystemConfig.key_value == chat_id,
                SystemConfig.tenant_id != tid,
            )
        ).scalar_one_or_none()
        if existing:
            raise HTTPException(status_code=400, detail="Telegram chat ID is already assigned to another tenant")
        _upsert_secret(db, tid, "telegram_chat_id", chat_id, is_secret=False, commit=False)
    db.commit()
    return get_telegram_config(current_user=current_user, db=db)


# ===== AI =====

class AIConfigResponse(BaseModel):
    ai_model: str | None = None
    ai_api_key: str | None = None


class AIConfigUpdate(BaseModel):
    ai_model: str | None = None
    ai_api_key: str | None = None


@router.get("/ai", response_model=AIConfigResponse)
def get_ai_config(
    current_user: User = Depends(require_permission("settings.read")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    api_key = _get_secret_masked(db, tid, "ai_api_key")
    return AIConfigResponse(
        ai_model=_get_config_value(db, tid, "ai_model", runtime_ai_config()["model"]),
        ai_api_key=api_key,
    )


@router.put("/ai", response_model=AIConfigResponse)
def update_ai_config(
    data: AIConfigUpdate,
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    if data.ai_model is not None:
        _upsert_secret(db, tid, "ai_model", data.ai_model, is_secret=False, commit=False)
    if data.ai_api_key is not None:
        _upsert_secret(db, tid, "ai_api_key", data.ai_api_key, is_secret=True, commit=False)
    db.commit()
    return get_ai_config(current_user=current_user, db=db)


# ===== Invoice Template =====

class InvoiceTemplateConfigResponse(BaseModel):
    template_html: str
    email_subject_template: str
    email_body_template: str
    available_variables: list[str] = Field(default_factory=list)


class InvoiceTemplateConfigUpdate(BaseModel):
    template_html: str
    email_subject_template: str
    email_body_template: str


@router.get("/invoice-template", response_model=InvoiceTemplateConfigResponse)
def get_invoice_template_config(
    current_user: User = Depends(require_permission("settings.read")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    return InvoiceTemplateConfigResponse(
        template_html=_get_config_value(db, tid, "invoice_template_html", DEFAULT_INVOICE_TEMPLATE),
        email_subject_template=_get_config_value(
            db,
            tid,
            "invoice_email_subject_template",
            DEFAULT_INVOICE_EMAIL_SUBJECT_TEMPLATE,
        ),
        email_body_template=_get_config_value(
            db,
            tid,
            "invoice_email_body_template",
            DEFAULT_INVOICE_EMAIL_BODY_TEMPLATE,
        ),
        available_variables=list(AVAILABLE_TEMPLATE_VARIABLES),
    )


@router.put("/invoice-template", response_model=InvoiceTemplateConfigResponse)
def update_invoice_template_config(
    data: InvoiceTemplateConfigUpdate,
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    _upsert_secret(db, tid, "invoice_template_html", data.template_html, is_secret=False, commit=False)
    _upsert_secret(
        db,
        tid,
        "invoice_email_subject_template",
        data.email_subject_template,
        is_secret=False,
        commit=False,
    )
    _upsert_secret(
        db,
        tid,
        "invoice_email_body_template",
        data.email_body_template,
        is_secret=False,
        commit=False,
    )
    db.commit()
    return get_invoice_template_config(current_user=current_user, db=db)


# ===== Payment Methods =====

class PaymentMethodsConfigResponse(BaseModel):
    payment_methods: list[str] = Field(default_factory=list)


class PaymentMethodsConfigUpdate(BaseModel):
    payment_methods: list[str] = Field(default_factory=list)


def _normalize_payment_methods(methods: list[str]) -> list[str]:
    normalized: list[str] = []
    for method in methods:
        value = method.strip()
        if value:
            normalized.append(value)
    return normalized or list(DEFAULT_PAYMENT_METHODS)


@router.get("/payment-methods", response_model=PaymentMethodsConfigResponse)
def get_payment_methods_config(
    current_user: User = Depends(require_permission("settings.read")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    raw = _get_config_value(db, tid, "invoice_payment_methods", ",".join(DEFAULT_PAYMENT_METHODS))
    methods = _normalize_payment_methods(
        [part for chunk in raw.splitlines() for part in chunk.split(",")]
    )
    return PaymentMethodsConfigResponse(payment_methods=methods)


@router.put("/payment-methods", response_model=PaymentMethodsConfigResponse)
def update_payment_methods_config(
    data: PaymentMethodsConfigUpdate,
    current_user: User = Depends(require_permission("settings.write")),
    db: Session = Depends(get_db),
):
    tid = current_user.tenant_id
    methods = _normalize_payment_methods(data.payment_methods)
    _upsert_secret(
        db,
        tid,
        "invoice_payment_methods",
        "\n".join(methods),
        is_secret=False,
        commit=False,
    )
    db.commit()
    return PaymentMethodsConfigResponse(payment_methods=methods)
