"""Quickstart step 2 (planned Python SDK) — download, upload, map, sync."""

import hashlib
import io
import json
import os
import time
import zipfile
from pathlib import Path

import httpx

from tessera import Tessera
from tessera._generated import models

client = Tessera(base_url=os.environ["TESSERA_API_BASE"])
state_dir = Path(os.environ.get("TESSERA_STATE_DIR", "."))
sample_url = os.environ.get(
    "TESSERA_SAMPLE_URL", "https://docs.staging.tessera.zone/downloads/tessera-sample-v1.zip"
)

# --- 1. download + verify the sample dataset ---------------------------------
sample_dir = state_dir / "sample"
sample_dir.mkdir(parents=True, exist_ok=True)
archive = zipfile.ZipFile(io.BytesIO(httpx.get(sample_url, follow_redirects=True).content))
archive.extractall(sample_dir)
manifest = json.loads((sample_dir / "manifest.json").read_text())
for name, meta in manifest["files"].items():
    digest = hashlib.sha256((sample_dir / name).read_bytes()).hexdigest()
    assert digest == meta["sha256"], f"checksum mismatch: {name}"
print("sample dataset verified:", ", ".join(sorted(manifest["files"])))

# --- 2. create an upload session and send the bytes ---------------------------
session = client.api.create_upload(
    models.UploadCreateRequest(filename="ecommerce_orders.csv", format="csv")
)
httpx.put(
    str(session.upload_url),
    content=(sample_dir / "ecommerce_orders.csv").read_bytes(),
).raise_for_status()
client.api.complete_upload(session.upload_id)

upload = client.api.get_upload(session.upload_id)
source_id = str(upload.source_id)
print(f"upload: {upload.status.value}")
for _ in range(300):
    # Poll the upload, not only the first sync: if that sync fails before
    # ingesting the file, Tessera hands it to one follow-up sync the upload names.
    upload = client.api.get_upload(session.upload_id)
    landing = client.syncs.get(str(upload.sync_id))
    if landing.status.value == "succeeded":
        break
    # The read that saw `failed` may itself have handed the file on: re-read the
    # upload once, and keep polling if it now names another sync.
    if (
        landing.status.value == "failed"
        and client.api.get_upload(session.upload_id).sync_id == upload.sync_id
    ):
        break
    time.sleep(1)
print(f"landing sync: {landing.status.value}")

# --- 3. map columns to canonical events ---------------------------------------
mapping = models.SourceMapping.model_validate(
    json.loads(Path("../quickstart/mapping-orders.json").read_text())
)
client.sources.put_mapping(source_id, mapping)
print("mapping stored")

# --- 4. re-sync: normalize every row into canonical events --------------------
sync = client.sources.create_sync(source_id, models.SyncCreateRequest(mode="full"))
for _ in range(300):
    sync = client.syncs.get(sync.sync_id)
    if sync.status.value in ("succeeded", "failed"):
        break
    time.sleep(1)
print(f"sync: {sync.status.value} rows_landed={sync.rows_landed} rejected={sync.rows_rejected}")

(state_dir / "source_id").write_text(source_id)
client.close()
