openapi: 3.1.0
info:
  title: Cadastrum Public API
  version: 1.2.0
  description: |
    Cadastrum API — gayrimenkul fiyat tahmin, emsal sorgu ve risk verisi.
    Kurumsal Pro tier'a özel programmatic erişim.

    Token formatı: `cdrm_<32-hex>`. /v1/api/tokens endpoint'inden üretilir.
    Rate limit varsayılan: 60 req/dakika/token (oluştururken özelleştirilebilir).

    **Değişiklik geçmişi:**
    - v1.2.0: /fiyat/trend, /fiyat/toplu-ozet, /fiyat/toplu-ilce-ozet, /sorgu endpoint'leri eklendi.
              /fiyat/{il,ilce} endpoint şemaları tamamlandı. Seed endpoint'leri (internal) belgelendi.
    - v1.1.0: İlk public release.
  contact:
    name: Cadastrum
    email: eparlak996@gmail.com
servers:
  - url: https://cadastrum-api.cadastrum-tr.workers.dev
    description: Production (Cloudflare Worker)
  - url: https://api.cadastrum.com.tr
    description: Production (custom domain)
security:
  - ApiKeyAuth: []

paths:

  # ── Sağlık ──────────────────────────────────────────────────────────────────

  /v1/health:
    get:
      tags: [Sistem]
      summary: Sağlık kontrolü
      security: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string, example: ok }
                  env: { type: string }
                  ts: { type: integer }

  # ── Fiyat ───────────────────────────────────────────────────────────────────

  /v1/fiyat/mahalle/{il}/{ilce}/{mahalle}:
    get:
      tags: [Fiyat]
      summary: Mahalle bazlı fiyat istatistiği
      description: |
        Scraper verileriyle hesaplanan mahalle medyanı. Veri yoksa AI baseline fallback döner.
      security: []
      parameters:
        - { name: il, in: path, required: true, schema: { type: string }, example: istanbul }
        - { name: ilce, in: path, required: true, schema: { type: string }, example: besiktas }
        - { name: mahalle, in: path, required: true, schema: { type: string }, example: levent }
        - name: kategori
          in: query
          schema: { type: string, default: arsa, enum: [arsa, tarla, konut, bahce, bag, zeytinlik] }
      responses:
        "200":
          description: Mahalle istatistik
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MahalleIstatistik' }
        "404":
          description: Veri yok

  /v1/fiyat/ilce/{il}/{ilce}:
    get:
      tags: [Fiyat]
      summary: İlçe bazlı fiyat istatistiği
      security: []
      parameters:
        - { name: il, in: path, required: true, schema: { type: string } }
        - { name: ilce, in: path, required: true, schema: { type: string } }
        - { name: kategori, in: query, schema: { type: string, default: arsa, enum: [arsa, tarla, konut, bahce, bag, zeytinlik] } }
      responses:
        "200":
          description: İlçe istatistik + mahalle listesi
          content:
            application/json:
              schema:
                type: object
                properties:
                  medyan: { type: number }
                  q1: { type: number }
                  q3: { type: number }
                  ilan_adet: { type: integer }
                  kaynak: { type: string, enum: [ilan-istatistik, ai-aggregate] }
                  mahalleler:
                    type: array
                    items:
                      type: object
                      properties:
                        mahalle_norm: { type: string }
                        medyan: { type: number }
                        ilan_adet: { type: integer }

  /v1/fiyat/il/{il}:
    get:
      tags: [Fiyat]
      summary: İl bazlı fiyat istatistiği
      security: []
      parameters:
        - { name: il, in: path, required: true, schema: { type: string } }
        - { name: kategori, in: query, schema: { type: string, default: arsa, enum: [arsa, tarla, konut, bahce, bag, zeytinlik] } }
      responses:
        "200":
          description: İl istatistik + ilçe listesi
          content:
            application/json:
              schema:
                type: object
                properties:
                  medyan: { type: number }
                  ilan_adet: { type: integer }
                  kaynak: { type: string, enum: [ilan-istatistik, ai-aggregate] }
                  ilceler:
                    type: array
                    items:
                      type: object
                      properties:
                        ilce_norm: { type: string }
                        medyan: { type: number }
                        ilan_adet: { type: integer }

  /v1/fiyat/toplu-ozet:
    get:
      tags: [Fiyat]
      summary: Tüm illerin medyan TL/m² özeti (harita choropleth için)
      description: Harita katmanı bu endpoint'ten beslenir. CDN'de 2 saat cache'lenir.
      security: []
      parameters:
        - { name: kategori, in: query, schema: { type: string, default: arsa, enum: [arsa, tarla, konut, bahce, bag, zeytinlik] } }
      responses:
        "200":
          description: Tüm iller özeti
          content:
            application/json:
              schema:
                type: object
                properties:
                  kategori: { type: string }
                  ilSayisi: { type: integer }
                  iller:
                    type: array
                    items:
                      type: object
                      properties:
                        il_norm: { type: string }
                        medyan: { type: number }
                        ilan_adet: { type: integer }
                        kaynak: { type: string, enum: [ilan, ai-baseline] }
                  guncelleme: { type: string, format: date-time }

  /v1/fiyat/toplu-ilce-ozet/{il}:
    get:
      tags: [Fiyat]
      summary: Bir ilin tüm ilçelerinin medyan özeti
      security: []
      parameters:
        - { name: il, in: path, required: true, schema: { type: string } }
        - { name: kategori, in: query, schema: { type: string, default: arsa } }
      responses:
        "200":
          description: İlçe özet listesi
          content:
            application/json:
              schema:
                type: object
                properties:
                  il: { type: string }
                  kategori: { type: string }
                  ilceler:
                    type: array
                    items:
                      type: object
                      properties:
                        ilce_norm: { type: string }
                        medyan: { type: number }
                        ilan_adet: { type: integer }
                        mahalle_sayi: { type: integer }

  /v1/fiyat/trend/{il}/{ilce}/{mahalle}:
    get:
      tags: [Fiyat]
      summary: Mahalle fiyat trendi + 6 aylık OLS projeksiyonu
      security: []
      parameters:
        - { name: il, in: path, required: true, schema: { type: string } }
        - { name: ilce, in: path, required: true, schema: { type: string } }
        - { name: mahalle, in: path, required: true, schema: { type: string } }
        - { name: kategori, in: query, schema: { type: string, default: arsa } }
      responses:
        "200":
          description: Trend verisi
          content:
            application/json:
              schema:
                type: object
                properties:
                  gecmis:
                    type: array
                    items: { $ref: '#/components/schemas/ZamanNoktasi' }
                  projeksiyon:
                    type: array
                    items:
                      type: object
                      properties:
                        yil: { type: integer }
                        ay: { type: integer }
                        tahmin: { type: number }
                        guven_alt: { type: number }
                        guven_ust: { type: number }
                  yillikDegisimYuzde: { type: number }
                  ruelDegisimYuzde: { type: number }
                  trend: { type: string, enum: [yukseliyor, dusuyor, duruyor] }
                  r2: { type: number }
                  aylikEgimTlm2: { type: number }
                  veriAyAdet: { type: integer }
                  seviye: { type: string, enum: [mahalle, ilce, il] }
        "404":
          description: Trend verisi yok

  # ── Koord Sorgu ─────────────────────────────────────────────────────────────

  /v1/sorgu:
    post:
      tags: [Sorgu]
      summary: Koordinat bazlı fiyat sorgusu (web app)
      description: |
        lat/lng koordinatına göre adaptif radius (5→10→20 km) ile emsal bazlı fiyat tahmini.
        Extension gerektirmez — web uygulaması için tasarlanmıştır.
        Rate limit: 20 req/saat/IP (Free).
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [lat, lng]
              properties:
                lat: { type: number, example: 41.0369 }
                lng: { type: number, example: 28.9850 }
                kategori: { type: string, default: arsa, enum: [arsa, tarla, konut] }
                m2: { type: number, description: Parsel alanı (toplam TL hesabı için) }
      responses:
        "200":
          description: Fiyat tahmini + emsal listesi
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  kaynak: { type: string, enum: [spatial-radius, mahalle-istatistik, il-fallback] }
                  guven_skoru: { type: integer, minimum: 0, maximum: 100 }
                  medyan_tlm2: { type: number }
                  alt_tlm2: { type: number }
                  ust_tlm2: { type: number }
                  radius_km: { type: number }
                  emsal_adet: { type: integer }
                  emsaller:
                    type: array
                    items: { $ref: '#/components/schemas/EmsalItem' }
                  toplam_tl:
                    nullable: true
                    type: object
                    properties:
                      alt: { type: number }
                      orta: { type: number }
                      ust: { type: number }

  # ── Emsal ───────────────────────────────────────────────────────────────────

  /v1/emsal/spatial:
    get:
      tags: [Emsal]
      summary: Koord bazlı spatial emsal sorgu
      parameters:
        - { name: lat, in: query, required: true, schema: { type: number } }
        - { name: lng, in: query, required: true, schema: { type: number } }
        - { name: radius_km, in: query, schema: { type: number, default: 5, maximum: 20 } }
        - { name: kategori, in: query, schema: { type: string, default: arsa } }
        - { name: mode, in: query, schema: { type: string, default: weighted_median, enum: [weighted_median, idw] } }
      responses:
        "200":
          description: Emsal listesi + weighted median
          content:
            application/json:
              schema:
                type: object
                properties:
                  lat: { type: number }
                  lng: { type: number }
                  radius_km: { type: number }
                  kategori: { type: string }
                  weighted_median: { type: number }
                  emsal_adet: { type: integer }
                  emsaller:
                    type: array
                    items: { $ref: '#/components/schemas/EmsalItem' }

  # ── Risk ────────────────────────────────────────────────────────────────────

  /v1/api/risk/deprem:
    get:
      tags: [Risk]
      summary: İl bazlı deprem risk (PGA + zon)
      parameters:
        - { name: il, in: query, required: true, schema: { type: string } }
      responses:
        "200":
          description: Risk bilgisi
          content:
            application/json:
              schema:
                type: object
                properties:
                  il: { type: string }
                  pga: { type: number, description: Peak Ground Acceleration (g) }
                  zon: { type: string, enum: [Z1, Z2, Z3, Z4] }
                  fiyat_carpani: { type: number }

  /v1/api/risk/taskin:
    get:
      tags: [Risk]
      summary: İl bazlı taşkın risk
      parameters:
        - { name: il, in: query, required: true, schema: { type: string } }
      responses:
        "200":
          description: Taşkın risk bilgisi
          content:
            application/json:
              schema:
                type: object
                properties:
                  il: { type: string }
                  risk: { type: string, enum: [yuksek, orta, dusuk] }
                  fiyat_carpani: { type: number }

  # ── Public API Tokens ────────────────────────────────────────────────────────

  /v1/api/tokens:
    post:
      tags: [Tokens]
      summary: Yeni API token oluştur
      description: |
        Kurumsal Pro tier gereklidir. JWT Bearer auth (kullanıcı girişi) ile çağrılır.
        Token bir kez gösterilir — saklayın.
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ad]
              properties:
                ad: { type: string, description: 'Token etiketi (örn "prod-backend")' }
                rate_limit_per_min: { type: integer, default: 60, minimum: 1, maximum: 600 }
      responses:
        "201":
          description: Token oluşturuldu
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  token: { type: string, example: "cdrm_a1b2c3d4e5f6..." }
                  prefix: { type: string, example: "cdrm_a1b2" }
                  id: { type: integer }
        "401":
          description: JWT yok
        "403":
          description: Kurumsal Pro tier gerekli
    get:
      tags: [Tokens]
      summary: API token listesi
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Token listesi (hash gizli, prefix gösterilir)
          content:
            application/json:
              schema:
                type: object
                properties:
                  tokenlar:
                    type: array
                    items:
                      type: object
                      properties:
                        id: { type: integer }
                        ad: { type: string }
                        prefix: { type: string }
                        rate_limit_per_min: { type: integer }
                        olusturuldu: { type: integer }
                        son_kullanim: { type: integer, nullable: true }

  /v1/api/tokens/{id}:
    delete:
      tags: [Tokens]
      summary: Token iptal et
      security:
        - BearerAuth: []
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "200":
          description: İptal edildi
        "404":
          description: Token yok

  # ── Auth (referans) ──────────────────────────────────────────────────────────
  # Bu endpoint'ler X-API-Key gerektirmez, JWT Bearer kullanır.
  # Tam dokümantasyon site/src/pages/api-docs.astro içindedir.

  /v1/auth/kayit:
    post:
      tags: [Auth]
      summary: Yeni kullanıcı kaydı
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, sifre]
              properties:
                email: { type: string, format: email }
                sifre: { type: string, minLength: 8 }
                ad: { type: string }
      responses:
        "200":
          description: Kayıt başarılı
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AuthCevap' }
        "409":
          description: Email zaten kayıtlı

  /v1/auth/giris:
    post:
      tags: [Auth]
      summary: Giriş yap + JWT al
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, sifre]
              properties:
                email: { type: string, format: email }
                sifre: { type: string }
      responses:
        "200":
          description: Giriş başarılı
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AuthCevap' }
        "401":
          description: Hatalı email/şifre
        "429":
          description: Rate limit (10 deneme/dakika)

# ── Components ────────────────────────────────────────────────────────────────

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        Kurumsal Pro tier API anahtarı.
        Format: `cdrm_<32-hex>`. /v1/api/tokens endpoint'inden oluşturulur.
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        JWT token — /v1/auth/giris endpoint'inden alınır.
        /v1/api/tokens CRUD işlemleri için gereklidir.

  schemas:
    MahalleIstatistik:
      type: object
      properties:
        kaynak: { type: string, enum: [ilan-istatistik, ai-baseline] }
        medyan: { type: number, description: TL/m² medyan fiyat }
        q1: { type: number }
        q3: { type: number }
        ortalama: { type: number, nullable: true }
        ilan_adet: { type: integer }
        son_guncelleme: { type: integer, description: Unix timestamp (ms) }
        trend:
          type: array
          items: { $ref: '#/components/schemas/ZamanNoktasi' }

    ZamanNoktasi:
      type: object
      properties:
        yil: { type: integer }
        ay: { type: integer }
        medyan: { type: number }
        ilan_adet: { type: integer }

    EmsalItem:
      type: object
      properties:
        fiyat_per_m2: { type: number }
        m2: { type: number, nullable: true }
        mesafe_m: { type: number, nullable: true }
        mahalle: { type: string, nullable: true }
        imar: { type: string, nullable: true }
        yas_gun: { type: integer }

    AuthCevap:
      type: object
      properties:
        token: { type: string, description: JWT (30 gün geçerli) }
        kullanici:
          type: object
          properties:
            id: { type: integer }
            email: { type: string }
            ad: { type: string, nullable: true }
            tier: { type: string, enum: [free, pro, pro_plus, kurumsal] }
            tierBitis: { type: integer, nullable: true }
            emailDogrulandi: { type: boolean }

    Hata:
      type: object
      properties:
        error: { type: string }
        hata: { type: string }
