"""
Seed programmatic SEO content: article templates + city data rows.

Inserts two article templates (EN + DE) and 18 cities × 2 language rows
into the database. Optionally runs the full generation pipeline to produce
pre-built HTML files and articles rows.

Usage:
    # Seed templates + data rows only (then trigger generation in admin):
    uv run python -m padelnomics.scripts.seed_content

    # Seed + generate articles immediately (all published, backdated 30 days):
    uv run python -m padelnomics.scripts.seed_content --generate
"""

import asyncio
import json
import logging
import os
import sqlite3
import sys
from datetime import UTC, date, datetime, timedelta
from pathlib import Path

from dotenv import load_dotenv

logger = logging.getLogger(__name__)

load_dotenv()

DATABASE_PATH = os.getenv("DATABASE_PATH", "data/app.db")

# =============================================================================
# Input schema — shared between EN and DE templates.
# Fields whose "name" matches a calculator DEFAULTS key are auto-passed to
# the financial model at generation time; all others are display-only.
# =============================================================================

INPUT_SCHEMA = [
    {"name": "city", "label": "City Name", "field_type": "text", "required": True},
    {"name": "city_slug", "label": "City Slug", "field_type": "text", "required": True},
    {"name": "country", "label": "Country Code (DE/US/GB)", "field_type": "text", "required": True},
    {"name": "country_name", "label": "Country Name", "field_type": "text", "required": True},
    {"name": "region", "label": "Region (for hub grouping)", "field_type": "text", "required": True},
    {"name": "language", "label": "Language (en/de)", "field_type": "text", "required": True},
    {"name": "population", "label": "Population (display)", "field_type": "text", "required": False},
    {"name": "padel_context", "label": "Padel Market Context (1-2 sentences)", "field_type": "text", "required": True},
    {"name": "currency_note", "label": "Currency Disclaimer (non-EUR markets)", "field_type": "text", "required": False},
    # --- Calc overrides (names must match DEFAULTS keys) ---
    {"name": "venue", "label": "Venue Type (indoor/outdoor)", "field_type": "text", "required": False},
    {"name": "own", "label": "Ownership (rent/buy)", "field_type": "text", "required": False},
    {"name": "dblCourts", "label": "Double Courts", "field_type": "number", "required": False},
    {"name": "sglCourts", "label": "Single Courts", "field_type": "number", "required": False},
    {"name": "ratePeak", "label": "Peak Rate (EUR/hr)", "field_type": "number", "required": False},
    {"name": "rateOffPeak", "label": "Off-Peak Rate (EUR/hr)", "field_type": "number", "required": False},
    {"name": "rentSqm", "label": "Commercial Rent (EUR/sqm/month)", "field_type": "number", "required": False},
    {"name": "electricity", "label": "Electricity (EUR/month)", "field_type": "number", "required": False},
    {"name": "heating", "label": "Heating (EUR/month)", "field_type": "number", "required": False},
    {"name": "staff", "label": "Staff Cost (EUR/month)", "field_type": "number", "required": False},
    {"name": "permitsCompliance", "label": "Permits & Compliance (EUR)", "field_type": "number", "required": False},
    {"name": "utilTarget", "label": "Utilisation Target (%)", "field_type": "number", "required": False},
]

# =============================================================================
# Article body templates (Jinja2 Markdown).
# Uses only standard Jinja2 built-in filters — no custom extensions.
# [scenario:slug:section] markers are replaced with rendered HTML cards
# at generation time by bake_scenario_cards().
# =============================================================================

_EN_BODY = """\
{{ padel_context }}

This analysis models a **{{ dblCourts }}-double + {{ sglCourts }}-single court indoor facility** \
using local market data for {{ city }}, {{ country_name }}.
{% if currency_note %}
> **Note:** {{ currency_note }}
{% endif %}
## Investment Overview

[scenario:{{ scenario_slug }}]

## Capital Expenditure Breakdown

[scenario:{{ scenario_slug }}:capex]

Court equipment, installation, and fit-out are the primary cost drivers. \
In {{ city }}, commercial property rents at approximately €{{ rentSqm }}/sqm per month. \
Permits and regulatory compliance for a {{ country_name }} venue total €{{ permitsCompliance | int }}.

## Revenue Model & Operating Costs

[scenario:{{ scenario_slug }}:operating]

At a peak rate of €{{ ratePeak }}/hour and off-peak at €{{ rateOffPeak }}/hour, \
with a {{ utilTarget }}% utilisation target, the model above shows the steady-state monthly \
P&L once the initial ramp-up period is complete.

## 5-Year Cash Flow Projection

[scenario:{{ scenario_slug }}:cashflow]

The projection accounts for a 12-month ramp-up curve as the venue builds its customer base. \
The cumulative cash flow column shows when the investment turns cash-positive.

## Returns & Exit Analysis

[scenario:{{ scenario_slug }}:returns]

Return metrics reflect a 5-year hold period with exit valued at a multiple of stabilised EBITDA — \
a standard assumption in padel and leisure real estate transactions across {{ country_name }}.

## Key Takeaways for {{ city }}

- **Commercial rent:** €{{ rentSqm }}/sqm per month — {% if rentSqm | float >= 6 %}above{% elif rentSqm | float <= 3.5 %}below{% else %}near{% endif %} the European average for padel facilities
- **Peak court rate:** €{{ ratePeak }}/hour reflects local demand and competitive positioning
- **Utilisation target:** {{ utilTarget }}% — achievable for a well-located {{ city }} venue in its second year
- **Permit costs:** €{{ permitsCompliance | int }} — representative of the {{ country_name }} regulatory environment

## Model Your Own {{ city }} Scenario

Every market is different. Use the free Padelnomics financial planner to adjust court count, \
pricing, rent, financing terms, and see how the numbers change in real time.\
"""

_DE_BODY = """\
{{ padel_context }}

Diese Analyse modelliert eine **{{ dblCourts }} Doppel- + {{ sglCourts }} Einzelcourt-Indoorhalle** \
auf Basis lokaler Marktdaten für {{ city }}, {{ country_name }}.
{% if currency_note %}
> **Hinweis:** {{ currency_note }}
{% endif %}
## Investitionsübersicht

[scenario:{{ scenario_slug }}]

## Investitionskosten (CAPEX)

[scenario:{{ scenario_slug }}:capex]

Courtausstattung, Installation und Innenausbau sind die größten Kostentreiber. \
In {{ city }} liegen Gewerbeimmobilien bei ca. €{{ rentSqm }}/m² pro Monat. \
Genehmigungen und Compliance für eine {{ country_name }}er Anlage belaufen sich auf €{{ permitsCompliance | int }}.

## Umsatzmodell & Betriebskosten

[scenario:{{ scenario_slug }}:operating]

Bei einem Peak-Preis von €{{ ratePeak }}/Stunde und Off-Peak von €{{ rateOffPeak }}/Stunde \
sowie einem Auslastungsziel von {{ utilTarget }}% zeigt das Modell die monatliche GuV \
nach Abschluss der Anlaufphase.

## 5-Jahres-Cashflow-Projektion

[scenario:{{ scenario_slug }}:cashflow]

Die Projektion berücksichtigt eine 12-monatige Anlaufkurve, während die Anlage ihren Kundenstamm aufbaut. \
Die kumulative Cashflow-Spalte zeigt, wann die Investition den Break-even erreicht.

## Rendite- & Exit-Analyse

[scenario:{{ scenario_slug }}:returns]

Die Renditekennzahlen basieren auf einem 5-Jahres-Haltezeitraum, wobei der Exit auf einem Vielfachen \
des stabilisierten EBITDA bewertet wird — eine gängige Annahme bei Padel- und Freizeitimmobilien in {{ country_name }}.

## Fazit für {{ city }}

- **Gewerbemiete:** €{{ rentSqm }}/m²/Monat — {% if rentSqm | float >= 6 %}über{% elif rentSqm | float <= 3.5 %}unter{% else %}um{% endif %} dem europäischen Durchschnitt für Padelanlagen
- **Peak-Courtpreis:** €{{ ratePeak }}/Stunde entspricht der lokalen Nachfrage und Marktpositionierung
- **Auslastungsziel:** {{ utilTarget }}% — erreichbar für eine gut gelegene {{ city }}er Anlage im zweiten Betriebsjahr
- **Genehmigungskosten:** €{{ permitsCompliance | int }} — repräsentativ für das {{ country_name }}e Regulierungsumfeld

## Ihr eigenes {{ city }}-Szenario modellieren

Jeder Markt ist anders. Nutzen Sie den kostenlosen Padelnomics Finanzplaner, um Courtanzahl, \
Preise, Miete und Finanzierungskonditionen anzupassen und die Zahlen in Echtzeit zu sehen.\
"""

TEMPLATES = [
    {
        "name": "City Padel Cost Analysis (EN)",
        "slug": "city-padel-cost-en",
        "content_type": "calculator",
        "input_schema": json.dumps(INPUT_SCHEMA),
        "url_pattern": "/padel-cost/{{ city_slug }}",
        "title_pattern": "Padel Court Cost in {{ city }}, {{ country_name }} — Investment Analysis 2026",
        "meta_description_pattern": (
            "How much does it cost to open a padel center in {{ city }}? "
            "Full investment breakdown: CAPEX, revenue model, and ROI analysis "
            "for a {{ dblCourts }}+{{ sglCourts }} court indoor venue."
        ),
        "body_template": _EN_BODY,
    },
    {
        "name": "City Padel Cost Analysis (DE)",
        "slug": "city-padel-cost-de",
        "content_type": "calculator",
        "input_schema": json.dumps(INPUT_SCHEMA),
        "url_pattern": "/padel-kosten/{{ city_slug }}",
        "title_pattern": "Padelhalle Kosten {{ city }}, {{ country_name }} — Investitionsanalyse 2026",
        "meta_description_pattern": (
            "Was kostet es, eine Padelhalle in {{ city }} zu eröffnen? "
            "Vollständige Investitionsanalyse: CAPEX, Umsatzmodell und Rendite "
            "für eine {{ dblCourts }}+{{ sglCourts }} Court Indoorhalle."
        ),
        "body_template": _DE_BODY,
    },
]

# =============================================================================
# City data.
# Each entry generates two template_data rows — one per template (EN + DE).
# Fields matching calculator DEFAULTS keys become calc overrides at generation.
# =============================================================================

# Currency note used for all non-EUR markets
_EUR_NOTE_EN = (
    "All projections are modelled in EUR for cross-market comparability. "
    "Use the planner to adjust inputs for local currency costs."
)
_EUR_NOTE_DE = (
    "Alle Projektionen sind in EUR für den Marktvergleich dargestellt. "
    "Nutzen Sie den Planer, um Eingaben auf lokale Währungskosten anzupassen."
)

CITIES = [
    # ------------------------------------------------------------------ Germany
    {
        "city": "Berlin",
        "city_slug_en": "berlin",
        "city_slug_de": "berlin",
        "country": "DE",
        "country_name_en": "Germany",
        "country_name_de": "Deutschland",
        "region": "Europe",
        "population": "3.6M",
        "padel_context_en": (
            "Berlin is Germany's leading padel market, with growing demand driven by "
            "a young, sports-active population and a dense network of sports facilities. "
            "The city's large commercial real estate stock keeps rents moderate relative "
            "to Munich or Frankfurt."
        ),
        "padel_context_de": (
            "Berlin ist Deutschlands führender Padelmarkt mit wachsender Nachfrage, "
            "getrieben von einer jungen, sportbegeisterten Bevölkerung und einem dichten "
            "Netz an Sportanlagen. Das große Gewerbeimmobilienangebot hält die Mieten "
            "moderat im Vergleich zu München oder Frankfurt."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 50, "rateOffPeak": 35,
        "rentSqm": 5, "electricity": 650, "heating": 450,
        "staff": 2000, "permitsCompliance": 12000, "utilTarget": 40,
    },
    {
        "city": "Munich",
        "city_slug_en": "munich",
        "city_slug_de": "muenchen",
        "country": "DE",
        "country_name_en": "Germany",
        "country_name_de": "Deutschland",
        "region": "Europe",
        "population": "1.5M",
        "padel_context_en": (
            "Munich is Germany's highest-spending padel market. Its affluent, "
            "sports-oriented population supports premium court rates, and the city "
            "already hosts over a dozen padel facilities with strong year-round demand."
        ),
        "padel_context_de": (
            "München ist Deutschlands umsatzstärkster Padelmarkt. Die kaufkräftige, "
            "sportbegeisterte Bevölkerung unterstützt Premium-Courtpreise, und die "
            "Stadt beherbergt bereits mehr als ein Dutzend Padelanlagen mit starker "
            "ganzjähriger Nachfrage."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 60, "rateOffPeak": 42,
        "rentSqm": 7, "electricity": 750, "heating": 500,
        "staff": 2500, "permitsCompliance": 15000, "utilTarget": 45,
    },
    {
        "city": "Hamburg",
        "city_slug_en": "hamburg",
        "city_slug_de": "hamburg",
        "country": "DE",
        "country_name_en": "Germany",
        "country_name_de": "Deutschland",
        "region": "Europe",
        "population": "1.9M",
        "padel_context_en": (
            "Hamburg has seen rapid padel growth, fuelled by its large expatriate "
            "community and proximity to the Scandinavian padel tradition. "
            "Commercial rents in suitable industrial-conversion zones remain attractive."
        ),
        "padel_context_de": (
            "Hamburg verzeichnet ein rasantes Padelwachstum, angetrieben durch seine "
            "große Expat-Gemeinde und die Nähe zur skandinavischen Padeltradition. "
            "Gewerbeimmobilienmieten in geeigneten Industriekonversionszonen "
            "bleiben attraktiv."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 55, "rateOffPeak": 38,
        "rentSqm": 6, "electricity": 700, "heating": 500,
        "staff": 2200, "permitsCompliance": 13000, "utilTarget": 42,
    },
    {
        "city": "Frankfurt",
        "city_slug_en": "frankfurt",
        "city_slug_de": "frankfurt",
        "country": "DE",
        "country_name_en": "Germany",
        "country_name_de": "Deutschland",
        "region": "Europe",
        "population": "760K",
        "padel_context_en": (
            "Frankfurt's financial sector workforce and high disposable incomes make it "
            "a natural fit for padel. The city's compact size means a smaller but highly "
            "engaged target market with premium spending power."
        ),
        "padel_context_de": (
            "Frankfurts Finanzsektor-Belegschaft und hohes verfügbares Einkommen machen "
            "die Stadt zu einem natürlichen Padelstandort. Die kompakte Größe der Stadt "
            "bedeutet einen kleineren, aber hochengagierten Zielmarkt mit Premium-Kaufkraft."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 1,
        "ratePeak": 58, "rateOffPeak": 40,
        "rentSqm": 7, "electricity": 700, "heating": 480,
        "staff": 2400, "permitsCompliance": 14000, "utilTarget": 43,
    },
    {
        "city": "Cologne",
        "city_slug_en": "cologne",
        "city_slug_de": "koeln",
        "country": "DE",
        "country_name_en": "Germany",
        "country_name_de": "Deutschland",
        "region": "Europe",
        "population": "1.1M",
        "padel_context_en": (
            "Cologne is an emerging padel market with several new clubs opening "
            "in the past two years. The city's large student population and active "
            "sports culture create a broad potential customer base."
        ),
        "padel_context_de": (
            "Köln ist ein aufstrebender Padelmarkt, in dem in den letzten zwei Jahren "
            "mehrere neue Clubs eröffnet haben. Die große Studentenschaft und aktive "
            "Sportkultur der Stadt schaffen eine breite potenzielle Kundenbasis."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 48, "rateOffPeak": 34,
        "rentSqm": 4.5, "electricity": 620, "heating": 420,
        "staff": 1800, "permitsCompliance": 11000, "utilTarget": 38,
    },
    {
        "city": "Düsseldorf",
        "city_slug_en": "dusseldorf",
        "city_slug_de": "duesseldorf",
        "country": "DE",
        "country_name_en": "Germany",
        "country_name_de": "Deutschland",
        "region": "Europe",
        "population": "640K",
        "padel_context_en": (
            "Düsseldorf's fashion and business community has embraced padel as a "
            "networking sport. Several corporate-backed clubs are already operating, "
            "indicating strong B2B membership potential."
        ),
        "padel_context_de": (
            "Düsseldorfs Mode- und Geschäftswelt hat Padel als Networking-Sport angenommen. "
            "Mehrere unternehmensgestützte Clubs sind bereits in Betrieb, "
            "was auf ein starkes B2B-Mitgliedschaftspotenzial hinweist."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 3, "sglCourts": 2,
        "ratePeak": 52, "rateOffPeak": 36,
        "rentSqm": 5.5, "electricity": 660, "heating": 440,
        "staff": 2100, "permitsCompliance": 12000, "utilTarget": 40,
    },
    {
        "city": "Stuttgart",
        "city_slug_en": "stuttgart",
        "city_slug_de": "stuttgart",
        "country": "DE",
        "country_name_en": "Germany",
        "country_name_de": "Deutschland",
        "region": "Europe",
        "population": "630K",
        "padel_context_en": (
            "Stuttgart's engineering and automotive workforce has strong disposable "
            "income and a culture of team sports. The city's hilly terrain limits "
            "outdoor options, making indoor padel particularly attractive."
        ),
        "padel_context_de": (
            "Stuttgarts Ingenieur- und Automobilbelegschaft verfügt über hohes "
            "verfügbares Einkommen und eine Kultur des Teamsports. Die hügelige "
            "Topografie der Stadt schränkt Outdoor-Optionen ein und macht Indoor-Padel "
            "besonders attraktiv."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 1,
        "ratePeak": 55, "rateOffPeak": 38,
        "rentSqm": 6, "electricity": 680, "heating": 460,
        "staff": 2300, "permitsCompliance": 13000, "utilTarget": 42,
    },
    {
        "city": "Leipzig",
        "city_slug_en": "leipzig",
        "city_slug_de": "leipzig",
        "country": "DE",
        "country_name_en": "Germany",
        "country_name_de": "Deutschland",
        "region": "Europe",
        "population": "600K",
        "padel_context_en": (
            "Leipzig offers the most cost-effective padel entry point in Germany. "
            "Low commercial rents and a growing young professional population make it "
            "attractive for first-mover operators willing to build the market."
        ),
        "padel_context_de": (
            "Leipzig bietet den kosteneffizientesten Padeleinstieg in Deutschland. "
            "Niedrige Gewerbemieten und eine wachsende junge Berufsschicht machen es "
            "attraktiv für Erstanbieter, die bereit sind, den Markt aufzubauen."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 3, "sglCourts": 2,
        "ratePeak": 42, "rateOffPeak": 30,
        "rentSqm": 3.5, "electricity": 550, "heating": 400,
        "staff": 1500, "permitsCompliance": 10000, "utilTarget": 35,
    },
    # ------------------------------------------------------------------ USA
    {
        "city": "Miami",
        "city_slug_en": "miami",
        "city_slug_de": "miami",
        "country": "US",
        "country_name_en": "USA",
        "country_name_de": "USA",
        "region": "North America",
        "population": "440K (metro 6.1M)",
        "padel_context_en": (
            "Miami leads the US padel boom, fuelled by its large Latin American community "
            "and year-round warm weather. The city has more than 30 operating courts "
            "and demand continues to outstrip supply."
        ),
        "padel_context_de": (
            "Miami führt den US-Padelboom an, angetrieben durch seine große "
            "lateinamerikanische Gemeinschaft und das ganzjährig warme Wetter. "
            "Die Stadt hat mehr als 30 aktive Courts und die Nachfrage übersteigt "
            "weiterhin das Angebot."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 5, "sglCourts": 2,
        "ratePeak": 70, "rateOffPeak": 50,
        "rentSqm": 3.5, "electricity": 800, "heating": 100,
        "staff": 3500, "permitsCompliance": 20000, "utilTarget": 45,
    },
    {
        "city": "New York",
        "city_slug_en": "new-york",
        "city_slug_de": "new-york",
        "country": "US",
        "country_name_en": "USA",
        "country_name_de": "USA",
        "region": "North America",
        "population": "8.3M",
        "padel_context_en": (
            "New York City is the highest-cost padel market in North America, with premium "
            "rates justified by extreme demand. Early movers are achieving 80%+ utilisation, "
            "driving strong investor interest in new capacity."
        ),
        "padel_context_de": (
            "New York City ist der teuerste Padelmarkt Nordamerikas, mit Premium-Preisen "
            "gerechtfertigt durch extreme Nachfrage. Frühe Markteinsteiger erzielen über "
            "80% Auslastung und treiben starkes Investoreninteresse an neuen Kapazitäten."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 4, "sglCourts": 1,
        "ratePeak": 80, "rateOffPeak": 58,
        "rentSqm": 8, "electricity": 900, "heating": 600,
        "staff": 5000, "permitsCompliance": 30000, "utilTarget": 50,
    },
    {
        "city": "Los Angeles",
        "city_slug_en": "los-angeles",
        "city_slug_de": "los-angeles",
        "country": "US",
        "country_name_en": "USA",
        "country_name_de": "USA",
        "region": "North America",
        "population": "3.9M",
        "padel_context_en": (
            "Los Angeles combines a large Hispanic community, a wellness-oriented culture, "
            "and year-round outdoor lifestyle to create one of the fastest-growing padel "
            "markets in the country. Several celebrity-backed clubs have boosted visibility."
        ),
        "padel_context_de": (
            "Los Angeles kombiniert eine große hispanische Gemeinschaft, eine "
            "wellnessorientierte Kultur und einen ganzjährigen Outdoor-Lebensstil zu einem "
            "der am schnellsten wachsenden Padelmärkte des Landes. "
            "Mehrere prominentengestützte Clubs haben die Bekanntheit gesteigert."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 5, "sglCourts": 2,
        "ratePeak": 75, "rateOffPeak": 55,
        "rentSqm": 5, "electricity": 750, "heating": 150,
        "staff": 4000, "permitsCompliance": 25000, "utilTarget": 48,
    },
    {
        "city": "Dallas",
        "city_slug_en": "dallas",
        "city_slug_de": "dallas",
        "country": "US",
        "country_name_en": "USA",
        "country_name_de": "USA",
        "region": "North America",
        "population": "1.3M",
        "padel_context_en": (
            "Dallas offers one of the most affordable commercial real estate environments "
            "for padel in the US, combined with a growing Hispanic population and strong "
            "sports culture. Several clubs are already operational in the metro area."
        ),
        "padel_context_de": (
            "Dallas bietet eines der günstigsten Gewerbeimmobilienumfelder für Padel "
            "in den USA, kombiniert mit einer wachsenden hispanischen Bevölkerung und "
            "starker Sportkultur. Mehrere Clubs sind bereits in der Metropolregion aktiv."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 55, "rateOffPeak": 40,
        "rentSqm": 3, "electricity": 700, "heating": 200,
        "staff": 2800, "permitsCompliance": 15000, "utilTarget": 40,
    },
    {
        "city": "Austin",
        "city_slug_en": "austin",
        "city_slug_de": "austin",
        "country": "US",
        "country_name_en": "USA",
        "country_name_de": "USA",
        "region": "North America",
        "population": "960K",
        "padel_context_en": (
            "Austin's tech-affluent, fitness-obsessed demographic is an ideal fit "
            "for padel. The city's rapid population growth and lack of established "
            "padel venues creates a significant first-mover opportunity."
        ),
        "padel_context_de": (
            "Austins technikaffine, fitnessbegeisterte Bevölkerung passt ideal zu Padel. "
            "Das schnelle Bevölkerungswachstum und die fehlenden etablierten Padelanlagen "
            "schaffen eine erhebliche First-Mover-Chance."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 55, "rateOffPeak": 38,
        "rentSqm": 3, "electricity": 700, "heating": 150,
        "staff": 2500, "permitsCompliance": 12000, "utilTarget": 42,
    },
    {
        "city": "Chicago",
        "city_slug_en": "chicago",
        "city_slug_de": "chicago",
        "country": "US",
        "country_name_en": "USA",
        "country_name_de": "USA",
        "region": "North America",
        "population": "2.7M",
        "padel_context_en": (
            "Chicago's large Hispanic community and established racquet sports culture "
            "make it a natural padel market. The harsh winters drive demand for "
            "indoor facilities, reducing the outdoor competition that affects "
            "warmer-climate markets."
        ),
        "padel_context_de": (
            "Chicagos große hispanische Gemeinschaft und etablierte Schlägertsportkultur "
            "machen es zu einem natürlichen Padelmarkt. Die harten Winter treiben die "
            "Nachfrage nach Indoorhallen und reduzieren den Outdoor-Wettbewerb, "
            "der wärmere Märkte beeinflusst."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 60, "rateOffPeak": 42,
        "rentSqm": 4, "electricity": 750, "heating": 550,
        "staff": 3200, "permitsCompliance": 18000, "utilTarget": 38,
    },
    # ------------------------------------------------------------------ UK
    {
        "city": "London",
        "city_slug_en": "london",
        "city_slug_de": "london",
        "country": "GB",
        "country_name_en": "UK",
        "country_name_de": "Großbritannien",
        "region": "Europe",
        "population": "8.8M",
        "padel_context_en": (
            "London is the UK's most established padel market, with premium clubs "
            "operating in affluent areas. High commercial rents are offset by "
            "the city's exceptional spending power and the sport's growing social cachet."
        ),
        "padel_context_de": (
            "London ist der etablierteste Padelmarkt Großbritanniens, mit Premium-Clubs "
            "in wohlhabenden Stadtteilen. Hohe Gewerbemieten werden durch die "
            "außergewöhnliche Kaufkraft der Stadt und den wachsenden sozialen Status "
            "des Sports ausgeglichen."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 65, "rateOffPeak": 45,
        "rentSqm": 7, "electricity": 800, "heating": 500,
        "staff": 3500, "permitsCompliance": 15000, "utilTarget": 45,
    },
    {
        "city": "Manchester",
        "city_slug_en": "manchester",
        "city_slug_de": "manchester",
        "country": "GB",
        "country_name_en": "UK",
        "country_name_de": "Großbritannien",
        "region": "Europe",
        "population": "550K (metro 2.8M)",
        "padel_context_en": (
            "Manchester's strong sports culture, large student population, and "
            "lower commercial rents versus London make it an attractive second city "
            "for padel investment, with several clubs already gaining strong membership."
        ),
        "padel_context_de": (
            "Manchesters starke Sportkultur, große Studentenschaft und niedrigere "
            "Gewerbemieten gegenüber London machen es zu einer attraktiven zweiten "
            "Stadt für Padelinvestitionen, mit mehreren Clubs, die bereits starke "
            "Mitgliederzahlen aufbauen."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 50, "rateOffPeak": 35,
        "rentSqm": 4.5, "electricity": 650, "heating": 450,
        "staff": 2200, "permitsCompliance": 10000, "utilTarget": 40,
    },
    {
        "city": "Edinburgh",
        "city_slug_en": "edinburgh",
        "city_slug_de": "edinburgh",
        "country": "GB",
        "country_name_en": "UK",
        "country_name_de": "Großbritannien",
        "region": "Europe",
        "population": "540K",
        "padel_context_en": (
            "Edinburgh is an early-stage padel market with significant first-mover "
            "potential. Its affluent professional population, university community, "
            "and lack of existing padel supply create a compelling entry opportunity."
        ),
        "padel_context_de": (
            "Edinburgh ist ein Frühphasen-Padelmarkt mit erheblichem First-Mover-Potenzial. "
            "Die wohlhabende Berufsschicht, Universitätsgemeinde und fehlende bestehende "
            "Padeliversorgung schaffen eine überzeugende Einstiegsmöglichkeit."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 3, "sglCourts": 2,
        "ratePeak": 48, "rateOffPeak": 33,
        "rentSqm": 4, "electricity": 620, "heating": 480,
        "staff": 2000, "permitsCompliance": 9000, "utilTarget": 38,
    },
    {
        "city": "Birmingham",
        "city_slug_en": "birmingham",
        "city_slug_de": "birmingham",
        "country": "GB",
        "country_name_en": "UK",
        "country_name_de": "Großbritannien",
        "region": "Europe",
        "population": "1.1M",
        "padel_context_en": (
            "Birmingham's large and diverse population, strong sports heritage, "
            "and relatively affordable commercial property make it one of the UK's "
            "most promising regional padel markets outside London."
        ),
        "padel_context_de": (
            "Birminghams große und vielfältige Bevölkerung, starkes Sporterbe und "
            "relativ erschwingliche Gewerbeimmobilien machen es zu einem der "
            "vielversprechendsten regionalen Padelmärkte Großbritanniens außerhalb Londons."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 4, "sglCourts": 1,
        "ratePeak": 48, "rateOffPeak": 34,
        "rentSqm": 4, "electricity": 630, "heating": 440,
        "staff": 2100, "permitsCompliance": 10000, "utilTarget": 38,
    },
    # ------------------------------------------------------------------ Spain
    {
        "city": "Madrid",
        "city_slug_en": "madrid",
        "city_slug_de": "madrid",
        "country": "ES",
        "country_name_en": "Spain",
        "country_name_de": "Spanien",
        "region": "Europe",
        "population": "3.4M",
        "padel_context_en": (
            "Madrid is the world's densest padel market, with over 5,000 courts "
            "nationwide and the sport embedded in everyday culture. High utilisation "
            "rates and a deeply padel-literate customer base make Madrid one of the "
            "best-validated markets for new investment."
        ),
        "padel_context_de": (
            "Madrid ist der weltweit dichteste Padelmarkt mit über 5.000 Courts "
            "und dem Sport als fester Alltagskultur. Hohe Auslastungsraten und "
            "eine padelerfahrene Kundschaft machen Madrid zu einem der "
            "bestvalidiertesten Märkte für neue Investitionen."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 5, "sglCourts": 2,
        "ratePeak": 44, "rateOffPeak": 30,
        "rentSqm": 6, "electricity": 580, "heating": 50,
        "staff": 2200, "permitsCompliance": 25000, "utilTarget": 60,
    },
    {
        "city": "Barcelona",
        "city_slug_en": "barcelona",
        "city_slug_de": "barcelona",
        "country": "ES",
        "country_name_en": "Spain",
        "country_name_de": "Spanien",
        "region": "Europe",
        "population": "1.6M",
        "padel_context_en": (
            "Barcelona combines a mature padel culture with year-round outdoor "
            "conditions and strong tourism demand. Premium zones near the seafront "
            "command higher rates, while suburban clubs operate at lower cost with "
            "equally strong fill rates."
        ),
        "padel_context_de": (
            "Barcelona verbindet eine reife Padelkultur mit ganzjährig "
            "günstigen Outdoor-Bedingungen und starker Tourismusnachfrage. "
            "Premium-Zonen nahe der Küste erzielen höhere Preise, während "
            "Vorortclubs bei niedrigeren Kosten gleichstarke Belegung aufweisen."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 5, "sglCourts": 2,
        "ratePeak": 47, "rateOffPeak": 32,
        "rentSqm": 8, "electricity": 590, "heating": 0,
        "staff": 2300, "permitsCompliance": 22000, "utilTarget": 60,
    },
    {
        "city": "Valencia",
        "city_slug_en": "valencia",
        "city_slug_de": "valencia",
        "country": "ES",
        "country_name_en": "Spain",
        "country_name_de": "Spanien",
        "region": "Europe",
        "population": "800K",
        "padel_context_en": (
            "Valencia offers a compelling cost-to-utilisation ratio. Commercial "
            "rents are 30–40% below Madrid, warm winters allow year-round outdoor "
            "play, and the padel culture is well established — making it one of "
            "Spain's most attractive second-tier markets."
        ),
        "padel_context_de": (
            "Valencia bietet ein überzeugendes Kosten-Auslastungs-Verhältnis. "
            "Gewerbemieten liegen 30–40% unter Madrid, warme Winter ermöglichen "
            "ganzjähriges Outdoor-Spiel, und die Padelkultur ist fest etabliert — "
            "einer der attraktivsten spanischen Zweitplatzmärkte."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 38, "rateOffPeak": 26,
        "rentSqm": 5, "electricity": 550, "heating": 0,
        "staff": 1800, "permitsCompliance": 18000, "utilTarget": 55,
    },
    {
        "city": "Seville",
        "city_slug_en": "seville",
        "city_slug_de": "sevilla",
        "country": "ES",
        "country_name_en": "Spain",
        "country_name_de": "Spanien",
        "region": "Europe",
        "population": "690K",
        "padel_context_en": (
            "Seville's hot, dry climate enables year-round outdoor padel, dramatically "
            "lowering operating costs versus indoor-only markets. Strong local sports "
            "culture and lower land prices combine to produce attractive returns for "
            "early-mover operators."
        ),
        "padel_context_de": (
            "Sevillas heißes, trockenes Klima ermöglicht ganzjähriges Outdoor-Padel "
            "und senkt die Betriebskosten gegenüber reinen Innenmarktstandorten erheblich. "
            "Starke lokale Sportkultur und niedrigere Grundstückspreise ergeben "
            "attraktive Renditen für Erstanbieter."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 35, "rateOffPeak": 24,
        "rentSqm": 4, "electricity": 520, "heating": 0,
        "staff": 1600, "permitsCompliance": 20000, "utilTarget": 52,
    },
    {
        "city": "Málaga",
        "city_slug_en": "malaga",
        "city_slug_de": "malaga",
        "country": "ES",
        "country_name_en": "Spain",
        "country_name_de": "Spanien",
        "region": "Europe",
        "population": "580K",
        "padel_context_en": (
            "Málaga's status as a major expat and tourism hub creates dual revenue "
            "streams: local membership and high-rate tourist bookings. The city's "
            "tech scene and growing digital-nomad community add a further demographic "
            "of high-frequency casual players."
        ),
        "padel_context_de": (
            "Málagas Status als bedeutendes Expat- und Tourismuszentrum schafft "
            "zwei Einnahmequellen: lokale Mitgliedschaften und hochpreisige Touristenbuchungen. "
            "Die Tech-Szene und wachsende Digital-Nomad-Gemeinschaft der Stadt schaffen "
            "eine weitere Demografie hochfrequenter Gelegenheitsspieler."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 42, "rateOffPeak": 28,
        "rentSqm": 5, "electricity": 540, "heating": 0,
        "staff": 1700, "permitsCompliance": 18000, "utilTarget": 53,
    },
    # ------------------------------------------------------------------ France
    {
        "city": "Paris",
        "city_slug_en": "paris",
        "city_slug_de": "paris",
        "country": "FR",
        "country_name_en": "France",
        "country_name_de": "Frankreich",
        "region": "Europe",
        "population": "2.1M (metro 12M)",
        "padel_context_en": (
            "Paris is France's largest and fastest-growing padel market, with demand "
            "significantly outpacing supply. High disposable incomes, an appetite for "
            "premium sports experiences, and tight commercial space keep prices elevated "
            "but support strong utilisation."
        ),
        "padel_context_de": (
            "Paris ist Frankreichs größter und am schnellsten wachsender Padelmarkt, "
            "mit deutlich überschüssiger Nachfrage. Hohe verfügbare Einkommen, "
            "Nachfrage nach Premium-Sporterfahrungen und knappes Gewerbeflächenangebot "
            "halten die Preise hoch und stützen eine starke Auslastung."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 58, "rateOffPeak": 40,
        "rentSqm": 12, "electricity": 620, "heating": 330,
        "staff": 3000, "permitsCompliance": 15000, "utilTarget": 47,
    },
    {
        "city": "Lyon",
        "city_slug_en": "lyon",
        "city_slug_de": "lyon",
        "country": "FR",
        "country_name_en": "France",
        "country_name_de": "Frankreich",
        "region": "Europe",
        "population": "520K (metro 2.3M)",
        "padel_context_en": (
            "Lyon has emerged as one of France's most active provincial padel markets, "
            "backed by a large student and professional population. Lower rents than "
            "Paris and a growing club network make it an attractive second-city opportunity."
        ),
        "padel_context_de": (
            "Lyon hat sich als einer der aktivsten provinziellen Padelmärkte Frankreichs "
            "etabliert, getragen von einer großen Studenten- und Berufsschicht. "
            "Günstigere Mieten als Paris und ein wachsendes Clubnetzwerk machen "
            "es zu einer attraktiven Zweitstadtchance."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 42, "rateOffPeak": 30,
        "rentSqm": 6, "electricity": 560, "heating": 280,
        "staff": 2000, "permitsCompliance": 12000, "utilTarget": 40,
    },
    {
        "city": "Marseille",
        "city_slug_en": "marseille",
        "city_slug_de": "marseille",
        "country": "FR",
        "country_name_en": "France",
        "country_name_de": "Frankreich",
        "region": "Europe",
        "population": "870K",
        "padel_context_en": (
            "Marseille's Mediterranean climate reduces heating costs significantly "
            "and extends the viable outdoor season. As France's second city by size, "
            "it offers deep demographic reach at commercial rents well below Paris or Lyon."
        ),
        "padel_context_de": (
            "Marseilles Mittelmeerklima senkt die Heizkosten erheblich und verlängert "
            "die nutzbare Outdoor-Saison. Als zweitgrößte Stadt Frankreichs bietet sie "
            "demographische Tiefe bei Gewerbemieten weit unter Paris oder Lyon."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 38, "rateOffPeak": 27,
        "rentSqm": 5, "electricity": 540, "heating": 100,
        "staff": 1700, "permitsCompliance": 12000, "utilTarget": 40,
    },
    # ------------------------------------------------------------------ Italy
    {
        "city": "Milan",
        "city_slug_en": "milan",
        "city_slug_de": "mailand",
        "country": "IT",
        "country_name_en": "Italy",
        "country_name_de": "Italien",
        "region": "Europe",
        "population": "1.4M (metro 3.2M)",
        "padel_context_en": (
            "Milan leads Italy's padel expansion, fuelled by high disposable incomes "
            "and a business culture that treats sport as a social currency. Demand from "
            "corporate accounts is particularly strong, with firms booking courts "
            "for team activities and client entertainment."
        ),
        "padel_context_de": (
            "Mailand führt Italiens Padelexpansion an, angetrieben durch hohes "
            "verfügbares Einkommen und eine Geschäftskultur, die Sport als soziale "
            "Währung betrachtet. Die Nachfrage von Firmenkonten ist besonders stark, "
            "da Unternehmen Courts für Teamaktivitäten und Kundenunterhaltung buchen."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 52, "rateOffPeak": 36,
        "rentSqm": 9, "electricity": 730, "heating": 400,
        "staff": 2500, "permitsCompliance": 18000, "utilTarget": 46,
    },
    {
        "city": "Rome",
        "city_slug_en": "rome",
        "city_slug_de": "rom",
        "country": "IT",
        "country_name_en": "Italy",
        "country_name_de": "Italien",
        "region": "Europe",
        "population": "2.8M",
        "padel_context_en": (
            "Rome's mild winters and vast population base underpin a rapidly growing "
            "padel market. Outdoor courts operate profitably year-round, reducing "
            "CAPEX versus fully enclosed facilities. Central government facilities "
            "and sports club conversions dominate early supply."
        ),
        "padel_context_de": (
            "Roms milde Winter und riesige Bevölkerungsbasis stützen einen schnell "
            "wachsenden Padelmarkt. Outdoor-Courts arbeiten ganzjährig profitabel, "
            "was den CAPEX gegenüber vollständig geschlossenen Anlagen reduziert. "
            "Staatliche Anlagen und Sportclubumbauten dominieren das frühe Angebot."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 46, "rateOffPeak": 32,
        "rentSqm": 7, "electricity": 700, "heating": 200,
        "staff": 2200, "permitsCompliance": 20000, "utilTarget": 43,
    },
    # ---------------------------------------------- Netherlands
    {
        "city": "Amsterdam",
        "city_slug_en": "amsterdam",
        "city_slug_de": "amsterdam",
        "country": "NL",
        "country_name_en": "Netherlands",
        "country_name_de": "Niederlande",
        "region": "Europe",
        "population": "920K",
        "padel_context_en": (
            "Amsterdam combines a sports-active, internationally minded population "
            "with one of Europe's highest purchasing powers. The city's compact geography "
            "and limited available space push operators toward efficient indoor multi-court "
            "venues, justifying premium pricing."
        ),
        "padel_context_de": (
            "Amsterdam verbindet eine sportaktive, international ausgerichtete Bevölkerung "
            "mit einer der höchsten Kaufkräfte Europas. Die kompakte Geografie und begrenzte "
            "Verfügbarkeit von Räumlichkeiten drängen Betreiber zu effizienten Indoor-"
            "Mehrcourt-Anlagen und rechtfertigen Premium-Preise."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 54, "rateOffPeak": 38,
        "rentSqm": 10, "electricity": 660, "heating": 460,
        "staff": 2800, "permitsCompliance": 10000, "utilTarget": 43,
    },
    # ----------------------------------------------------------------- Austria
    {
        "city": "Vienna",
        "city_slug_en": "vienna",
        "city_slug_de": "wien",
        "country": "AT",
        "country_name_en": "Austria",
        "country_name_de": "Österreich",
        "region": "Europe",
        "population": "1.9M",
        "padel_context_en": (
            "Vienna is Austria's dominant padel market and one of Central Europe's "
            "fastest-growing cities for the sport. A large English-speaking expat "
            "community and high real incomes drive premium bookings, particularly "
            "in the first and third districts."
        ),
        "padel_context_de": (
            "Wien ist Österreichs dominanter Padelmarkt und eine der am schnellsten "
            "wachsenden Städte für den Sport in Mitteleuropa. Eine große englischsprachige "
            "Expat-Gemeinschaft und hohe Realeinkommen treiben Premium-Buchungen an, "
            "insbesondere im ersten und dritten Bezirk."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 50, "rateOffPeak": 35,
        "rentSqm": 7, "electricity": 610, "heating": 510,
        "staff": 2400, "permitsCompliance": 14000, "utilTarget": 42,
    },
    # --------------------------------------------------------------- Switzerland
    {
        "city": "Zurich",
        "city_slug_en": "zurich",
        "city_slug_de": "zuerich",
        "country": "CH",
        "country_name_en": "Switzerland",
        "country_name_de": "Schweiz",
        "region": "Europe",
        "population": "440K (metro 1.4M)",
        "padel_context_en": (
            "Zurich is Europe's most expensive padel market, driven by the highest "
            "average wages on the continent. Players expect premium facilities and "
            "are willing to pay for them — average court rates are 60–80% above "
            "comparable German cities. Staff costs are the single largest operating "
            "line item."
        ),
        "padel_context_de": (
            "Zürich ist Europas teuerster Padelmarkt, angetrieben durch die höchsten "
            "Durchschnittslöhne des Kontinents. Spieler erwarten Premium-Anlagen und "
            "sind bereit, dafür zu zahlen — durchschnittliche Courtpreise liegen 60–80% "
            "über vergleichbaren deutschen Städten. Personalkosten sind die größte "
            "Einzelposition im Betrieb."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 4, "sglCourts": 1,
        "ratePeak": 75, "rateOffPeak": 52,
        "rentSqm": 15, "electricity": 500, "heating": 480,
        "staff": 4500, "permitsCompliance": 15000, "utilTarget": 45,
    },
    # ------------------------------------------------------------------ Sweden
    {
        "city": "Stockholm",
        "city_slug_en": "stockholm",
        "city_slug_de": "stockholm",
        "country": "SE",
        "country_name_en": "Sweden",
        "country_name_de": "Schweden",
        "region": "Europe",
        "population": "980K",
        "padel_context_en": (
            "Stockholm has the highest padel court-per-capita ratio in the world outside "
            "Spain. The sport is deeply embedded in Swedish corporate and social culture — "
            "padel after work is a fixture for office teams. Indoor venues dominate "
            "given the long winter, but cheap electricity (largely hydropower) keeps "
            "operating costs manageable."
        ),
        "padel_context_de": (
            "Stockholm hat das weltweit höchste Padel-Court-pro-Kopf-Verhältnis außerhalb "
            "Spaniens. Der Sport ist tief in der schwedischen Unternehmens- und Sozialkultur "
            "verankert. Indoor-Anlagen dominieren aufgrund des langen Winters, aber günstige "
            "Elektrizität (größtenteils Wasserkraft) hält die Betriebskosten beherrschbar."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 62, "rateOffPeak": 44,
        "rentSqm": 8, "electricity": 390, "heating": 650,
        "staff": 3200, "permitsCompliance": 8000, "utilTarget": 46,
    },
    # ----------------------------------------------------------------- Portugal
    {
        "city": "Lisbon",
        "city_slug_en": "lisbon",
        "city_slug_de": "lissabon",
        "country": "PT",
        "country_name_en": "Portugal",
        "country_name_de": "Portugal",
        "region": "Europe",
        "population": "550K (metro 2.8M)",
        "padel_context_en": (
            "Lisbon is one of Europe's fastest-growing padel markets, driven by an "
            "influx of international residents and a warm climate that minimises "
            "heating costs. Court rates are rising rapidly as demand outpaces supply, "
            "making early-mover advantage particularly valuable."
        ),
        "padel_context_de": (
            "Lissabon ist einer der am schnellsten wachsenden Padelmärkte Europas, "
            "angetrieben durch einen Zustrom internationaler Einwohner und ein warmes "
            "Klima, das Heizkosten minimiert. Courtpreise steigen rapide, da die Nachfrage "
            "das Angebot übersteigt — der Erstanbieter-Vorteil ist besonders wertvoll."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 38, "rateOffPeak": 26,
        "rentSqm": 6, "electricity": 540, "heating": 80,
        "staff": 1700, "permitsCompliance": 12000, "utilTarget": 52,
    },
    {
        "city": "Porto",
        "city_slug_en": "porto",
        "city_slug_de": "porto",
        "country": "PT",
        "country_name_en": "Portugal",
        "country_name_de": "Portugal",
        "region": "Europe",
        "population": "240K (metro 1.7M)",
        "padel_context_en": (
            "Porto offers the most accessible padel investment entry point in Western Europe. "
            "Commercial rents are among the lowest of any major city in this report, "
            "the growing tech sector is driving demand, and the mild Atlantic climate "
            "supports year-round play without heating infrastructure."
        ),
        "padel_context_de": (
            "Porto bietet den günstigsten Padelinvestitions-Einstiegspunkt in Westeuropa. "
            "Gewerbemieten gehören zu den niedrigsten aller Großstädte in diesem Bericht, "
            "der wachsende Tech-Sektor treibt die Nachfrage, und das milde Atlantikklima "
            "unterstützt ganzjähriges Spiel ohne Heizinfrastruktur."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 3, "sglCourts": 2,
        "ratePeak": 32, "rateOffPeak": 22,
        "rentSqm": 4, "electricity": 500, "heating": 50,
        "staff": 1400, "permitsCompliance": 10000, "utilTarget": 48,
    },
    # ----------------------------------------------------------------- Belgium
    {
        "city": "Brussels",
        "city_slug_en": "brussels",
        "city_slug_de": "bruessel",
        "country": "BE",
        "country_name_en": "Belgium",
        "country_name_de": "Belgien",
        "region": "Europe",
        "population": "1.2M (metro 2.1M)",
        "padel_context_en": (
            "Brussels benefits from a large international civil-servant and lobbying "
            "community with high disposable incomes and a taste for premium leisure. "
            "Belgium's electricity costs are among Europe's highest, making energy "
            "efficiency a critical factor in site selection."
        ),
        "padel_context_de": (
            "Brüssel profitiert von einer großen internationalen Beamten- und "
            "Lobbyisten-Gemeinschaft mit hohem verfügbarem Einkommen. Belgiens "
            "Stromkosten gehören zu den höchsten Europas — Energieeffizienz ist "
            "ein entscheidender Faktor bei der Standortwahl."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 50, "rateOffPeak": 35,
        "rentSqm": 8, "electricity": 660, "heating": 450,
        "staff": 2600, "permitsCompliance": 12000, "utilTarget": 41,
    },
    # ------------------------------------------------------------------- UAE
    {
        "city": "Dubai",
        "city_slug_en": "dubai",
        "city_slug_de": "dubai",
        "country": "AE",
        "country_name_en": "UAE",
        "country_name_de": "Vereinigte Arabische Emirate",
        "region": "Middle East",
        "population": "3.7M",
        "padel_context_en": (
            "Dubai is the fastest-growing padel market in the Middle East, "
            "with investment from European club operators and locally founded chains "
            "expanding rapidly. Year-round outdoor conditions, no income tax, "
            "and a high-earning expat population create exceptional unit economics. "
            "AC costs replace heating but are heavily subsidised."
        ),
        "padel_context_de": (
            "Dubai ist der am schnellsten wachsende Padelmarkt im Nahen Osten, "
            "mit Investitionen europäischer Clubbetreiber und lokaler Ketten. "
            "Ganzjährige Outdoor-Bedingungen, keine Einkommensteuer und "
            "eine gut verdienende Expat-Bevölkerung schaffen außergewöhnliche Unit Economics. "
            "AC-Kosten ersetzen Heizung, sind aber stark subventioniert."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 6, "sglCourts": 2,
        "ratePeak": 85, "rateOffPeak": 58,
        "rentSqm": 5, "electricity": 380, "heating": 0,
        "staff": 4200, "permitsCompliance": 20000, "utilTarget": 58,
    },
    # ---------------------------------------------------------------- Australia
    {
        "city": "Sydney",
        "city_slug_en": "sydney",
        "city_slug_de": "sydney",
        "country": "AU",
        "country_name_en": "Australia",
        "country_name_de": "Australien",
        "region": "Asia Pacific",
        "population": "5.3M",
        "padel_context_en": (
            "Sydney is Australia's largest and most developed padel market, "
            "with supply ramping rapidly since 2022. High commercial rents and "
            "electricity costs require strong utilisation to break even, but the "
            "city's affluent coastal suburbs produce premium booking rates and "
            "membership uptake above European benchmarks."
        ),
        "padel_context_de": (
            "Sydney ist Australiens größter und am weitesten entwickelter Padelmarkt, "
            "mit rasant steigendem Angebot seit 2022. Hohe Gewerberaum- und Stromkosten "
            "erfordern eine starke Auslastung, aber die wohlhabenden Küstenvorortee "
            "der Stadt erzielen Premium-Buchungsraten über europäischen Benchmarks."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 5, "sglCourts": 2,
        "ratePeak": 68, "rateOffPeak": 48,
        "rentSqm": 6, "electricity": 760, "heating": 80,
        "staff": 3600, "permitsCompliance": 18000, "utilTarget": 50,
    },
    {
        "city": "Melbourne",
        "city_slug_en": "melbourne",
        "city_slug_de": "melbourne",
        "country": "AU",
        "country_name_en": "Australia",
        "country_name_de": "Australien",
        "region": "Asia Pacific",
        "population": "5.1M",
        "padel_context_en": (
            "Melbourne's four-seasons climate and strong indoor-sport infrastructure "
            "make it well suited to padel. The city trails Sydney in court density "
            "but benefits from somewhat lower commercial rents and a sports culture "
            "that is quick to adopt new racket sports."
        ),
        "padel_context_de": (
            "Melbournes Vier-Jahreszeiten-Klima und starke Hallensport-Infrastruktur "
            "machen es gut geeignet für Padel. Die Stadt liegt hinter Sydney in "
            "der Court-Dichte, profitiert aber von etwas niedrigeren Gewerbemieten "
            "und einer Sportkultur, die neue Racketsportarten schnell annimmt."
        ),
        "currency_note_en": _EUR_NOTE_EN,
        "currency_note_de": _EUR_NOTE_DE,
        "dblCourts": 5, "sglCourts": 2,
        "ratePeak": 62, "rateOffPeak": 44,
        "rentSqm": 5, "electricity": 700, "heating": 100,
        "staff": 3200, "permitsCompliance": 16000, "utilTarget": 47,
    },
    # ----------------------------------------------------------------- Ireland
    {
        "city": "Dublin",
        "city_slug_en": "dublin",
        "city_slug_de": "dublin",
        "country": "IE",
        "country_name_en": "Ireland",
        "country_name_de": "Irland",
        "region": "Europe",
        "population": "1.4M",
        "padel_context_en": (
            "Dublin's booming tech sector and large young professional population "
            "have driven rapid uptake of padel since 2022. Ireland's electricity "
            "costs are among Europe's highest, significantly impacting operating "
            "margins — energy-efficient site design is essential."
        ),
        "padel_context_de": (
            "Dublins boomender Tech-Sektor und eine große junge Berufsschicht "
            "haben seit 2022 eine rasche Padeladoption angetrieben. Irlands Stromkosten "
            "gehören zu den höchsten Europas und belasten die Betriebsmargen erheblich — "
            "energieeffizientes Standortdesign ist unerlässlich."
        ),
        "currency_note_en": "",
        "currency_note_de": "",
        "dblCourts": 4, "sglCourts": 2,
        "ratePeak": 55, "rateOffPeak": 38,
        "rentSqm": 9, "electricity": 800, "heating": 460,
        "staff": 2900, "permitsCompliance": 12000, "utilTarget": 42,
    },
]

# Calc fields that get passed to the financial model (must match DEFAULTS keys)
_CALC_FIELDS = {
    "venue", "own", "country",
    "dblCourts", "sglCourts",
    "ratePeak", "rateOffPeak",
    "rentSqm", "electricity", "heating", "staff",
    "permitsCompliance", "utilTarget",
}


def _build_row(city: dict, lang: str) -> dict:
    """Build a template_data data_json dict for one city + language."""
    is_de = lang == "de"
    row = {
        "city": city["city"],
        "city_slug": city["city_slug_de"] if is_de else city["city_slug_en"],
        "country": city["country"],
        "country_name": city["country_name_de"] if is_de else city["country_name_en"],
        "region": city["region"],
        "language": lang,
        "population": city.get("population", ""),
        "padel_context": city["padel_context_de"] if is_de else city["padel_context_en"],
        "currency_note": city.get("currency_note_de" if is_de else "currency_note_en", ""),
        "venue": "indoor",
        "own": "rent",
        "dblCourts": city["dblCourts"],
        "sglCourts": city["sglCourts"],
        "ratePeak": city["ratePeak"],
        "rateOffPeak": city["rateOffPeak"],
        "rentSqm": city["rentSqm"],
        "electricity": city["electricity"],
        "heating": city["heating"],
        "staff": city["staff"],
        "permitsCompliance": city["permitsCompliance"],
        "utilTarget": city["utilTarget"],
    }
    return row


# =============================================================================
# Seed logic (synchronous sqlite3)
# =============================================================================

def seed_templates(conn: sqlite3.Connection) -> dict[str, int]:
    """Insert article templates if they don't exist. Returns {slug: id} map."""
    template_ids: dict[str, int] = {}

    for tmpl in TEMPLATES:
        existing = conn.execute(
            "SELECT id FROM article_templates WHERE slug = ?", (tmpl["slug"],)
        ).fetchone()

        if existing:
            logger.info("  Template '%s' already exists (id=%s), skipping.", tmpl["slug"], existing[0])
            template_ids[tmpl["slug"]] = existing[0]
        else:
            cur = conn.execute(
                """INSERT INTO article_templates
                   (name, slug, content_type, input_schema,
                    url_pattern, title_pattern, meta_description_pattern, body_template)
                   VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
                (
                    tmpl["name"],
                    tmpl["slug"],
                    tmpl["content_type"],
                    tmpl["input_schema"],
                    tmpl["url_pattern"],
                    tmpl["title_pattern"],
                    tmpl["meta_description_pattern"],
                    tmpl["body_template"],
                ),
            )
            template_ids[tmpl["slug"]] = cur.lastrowid
            logger.info("  Created template '%s' (id=%s)", tmpl["slug"], cur.lastrowid)

    return template_ids


def seed_data_rows(conn: sqlite3.Connection, template_ids: dict[str, int]) -> int:
    """Insert template_data rows for all cities × languages. Returns count inserted."""
    now = datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S")
    inserted = 0

    en_id = template_ids.get("city-padel-cost-en")
    de_id = template_ids.get("city-padel-cost-de")

    for city in CITIES:
        for lang, tmpl_id in [("en", en_id), ("de", de_id)]:
            row_data = _build_row(city, lang)
            data_json = json.dumps(row_data)
            city_slug = row_data["city_slug"]

            # Check for existing row by matching city_slug inside data_json
            existing = conn.execute(
                """SELECT id FROM template_data
                   WHERE template_id = ?
                   AND json_extract(data_json, '$.city_slug') = ?""",
                (tmpl_id, city_slug),
            ).fetchone()

            if existing:
                logger.info("  Data row '%s' (%s) already exists, skipping.", city_slug, lang)
            else:
                conn.execute(
                    """INSERT INTO template_data (template_id, data_json, created_at)
                       VALUES (?, ?, ?)""",
                    (tmpl_id, data_json, now),
                )
                inserted += 1
                logger.info("  Inserted data row '%s' (%s)", city_slug, lang)

    return inserted


# =============================================================================
# Async generation (bootstraps the DB layer without full Quart app context)
# =============================================================================

async def generate_articles(template_ids: dict[str, int]) -> None:
    from padelnomics.admin.routes import _generate_from_template  # noqa: PLC0415
    from padelnomics.core import close_db, fetch_one, init_db

    logger.info("Initialising database connection...")
    await init_db(DATABASE_PATH)

    start_date = date.today() - timedelta(days=30)  # backdate so all are immediately live

    for slug, tmpl_id in template_ids.items():
        template = await fetch_one("SELECT * FROM article_templates WHERE id = ?", (tmpl_id,))
        assert template is not None, f"Template '{slug}' not found in DB"

        logger.info("Generating articles for template '%s'...", slug)
        count = await _generate_from_template(template, start_date, articles_per_day=3)
        logger.info("  Generated %s articles.", count)

    await close_db()


# =============================================================================
# Entry point
# =============================================================================

def main() -> None:
    assert Path(DATABASE_PATH).exists(), (
        f"Database not found at {DATABASE_PATH!r}. "
        "Run migrations first: uv run python -m padelnomics.migrations"
    )

    conn = sqlite3.connect(DATABASE_PATH)
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA foreign_keys=ON")
    conn.row_factory = sqlite3.Row

    logger.info("Seeding article templates...")
    template_ids = seed_templates(conn)

    logger.info("Seeding city data rows...")
    inserted = seed_data_rows(conn, template_ids)
    conn.commit()
    conn.close()

    logger.info("Done. %s data rows inserted.", inserted)
    logger.info("Templates and data rows are visible in admin -> Templates.")

    if "--generate" in sys.argv:
        logger.info("Running article generation pipeline...")
        asyncio.run(generate_articles(template_ids))
        logger.info("Generation complete. Check admin -> Articles.")
    else:
        logger.info(
            "To generate articles, either:\n"
            "  1. Run: uv run python -m padelnomics.scripts.seed_content --generate\n"
            "  2. Or visit admin -> Templates -> (template) -> Generate"
        )


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s")
    main()
