"""
AutoScout24.ch API Client

A production-ready Python client for scraping car listings from autoscout24.ch.
This client interacts with the official AutoScout24 API endpoints.

Author: Generated by Claude Code
Date: 2026-01-03
"""

import requests
from typing import Dict, List, Optional, Any
from dataclasses import dataclass
from enum import Enum


class VehicleCategory(Enum):
    """Vehicle category types"""
    CAR = "car"
    CAMPER = "camper"
    TRUCK = "truck"
    MOTORCYCLE = "motorcycle"
    TRAILER = "trailer"


class BodyType(Enum):
    """Vehicle body types"""
    SUV = "suv"
    SEDAN = "saloon"
    ESTATE = "estate"
    COUPE = "coupe"
    CABRIOLET = "cabriolet"
    SMALL_CAR = "small-car"
    PICKUP = "pickup"
    MINIVAN = "minivan"
    BUS = "bus"


class FuelType(Enum):
    """Fuel types"""
    ELECTRIC = "electric"
    PETROL = "petrol"
    DIESEL = "diesel"
    HYBRID = "hybrid"
    MHEV_PETROL = "mhev-petrol"
    MHEV_DIESEL = "mhev-diesel"
    PLUGIN_HYBRID = "plugin-hybrid"


class ConditionType(Enum):
    """Vehicle condition types"""
    NEW = "new"
    PRE_REGISTERED = "pre-registered"
    DEMONSTRATION = "demonstration"
    USED = "used"


class SortOrder(Enum):
    """Sort order"""
    ASC = "ASC"
    DESC = "DESC"


class SortType(Enum):
    """Sort types"""
    RELEVANCE = "RELEVANCE"
    PRICE = "PRICE"
    MILEAGE = "MILEAGE"
    YEAR = "YEAR"
    POWER = "POWER"


@dataclass
class SearchQuery:
    """Search query parameters"""
    vehicle_categories: Optional[List[VehicleCategory]] = None
    make_key: Optional[str] = None
    model_key: Optional[str] = None
    body_type: Optional[BodyType] = None
    condition_type: Optional[ConditionType] = None
    fuel_type: Optional[FuelType] = None
    price_from: Optional[int] = None
    price_to: Optional[int] = None
    year_from: Optional[int] = None
    year_to: Optional[int] = None
    mileage_from: Optional[int] = None
    mileage_to: Optional[int] = None
    power_from: Optional[int] = None  # in HP
    power_to: Optional[int] = None  # in HP
    seller_ids: Optional[List[int]] = None
    excluding_ids: Optional[List[int]] = None
    zip_code: Optional[str] = None
    radius_km: Optional[int] = None
    has_leasing_only: Optional[bool] = None
    has_warranty: Optional[bool] = None
    has_accident_history: Optional[bool] = None


@dataclass
class Pagination:
    """Pagination parameters"""
    page: int = 0
    size: int = 20  # Default size, max is typically 100


@dataclass
class Sort:
    """Sort parameters"""
    order: SortOrder = SortOrder.DESC
    type: SortType = SortType.RELEVANCE
    variant: str = "v1"


class AutoScout24Client:
    """
    AutoScout24 API Client for searching and retrieving car listings.

    This client provides access to the AutoScout24.ch API endpoints for:
    - Searching car listings with various filters
    - Getting detailed information about specific listings
    - Fetching seller information

    Example:
        client = AutoScout24Client()

        # Search for BMW X1 vehicles
        results = client.search_listings(
            make_key="bmw",
            model_key="x1",
            price_from=20000,
            price_to=50000
        )

        # Print results
        for listing in results.get('listings', []):
            print(f"{listing['title']} - CHF {listing['price']}")
    """

    BASE_URL = "https://api.autoscout24.ch"

    def __init__(self, language: str = "de"):
        """
        Initialize the AutoScout24 client.

        Args:
            language: Language code (de, fr, it, en). Defaults to "de".
        """
        self.language = language
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
            "Accept": "*/*",
            "Accept-Language": "en-US,en;q=0.9",
            "Content-Type": "application/json",
            "Origin": "https://www.autoscout24.ch",
            "Referer": "https://www.autoscout24.ch/"
        })

    def _build_search_query(self, **kwargs) -> Dict[str, Any]:
        """
        Build search query from keyword arguments.

        Args:
            **kwargs: Search parameters

        Returns:
            Dict containing the search query
        """
        query: Dict[str, Any] = {}

        # Vehicle categories
        if kwargs.get('vehicle_categories'):
            categories = kwargs['vehicle_categories']
            if not isinstance(categories, list):
                categories = [categories]
            query['vehicleCategories'] = [cat.value if isinstance(cat, VehicleCategory) else cat for cat in categories]

        # Make and model
        if kwargs.get('make_key'):
            query['makeKey'] = kwargs['make_key']
        if kwargs.get('model_key'):
            query['modelKey'] = kwargs['model_key']

        # Body type
        if kwargs.get('body_type'):
            body_type = kwargs['body_type']
            query['bodyType'] = body_type.value if isinstance(body_type, BodyType) else body_type

        # Condition type
        if kwargs.get('condition_type'):
            condition = kwargs['condition_type']
            query['conditionType'] = condition.value if isinstance(condition, ConditionType) else condition

        # Fuel type
        if kwargs.get('fuel_type'):
            fuel = kwargs['fuel_type']
            query['fuelType'] = fuel.value if isinstance(fuel, FuelType) else fuel

        # Price range
        if kwargs.get('price_from') is not None:
            query['priceFrom'] = kwargs['price_from']
        if kwargs.get('price_to') is not None:
            query['priceTo'] = kwargs['price_to']

        # Year range
        if kwargs.get('year_from') is not None:
            query['yearFrom'] = kwargs['year_from']
        if kwargs.get('year_to') is not None:
            query['yearTo'] = kwargs['year_to']

        # Mileage range
        if kwargs.get('mileage_from') is not None:
            query['mileageFrom'] = kwargs['mileage_from']
        if kwargs.get('mileage_to') is not None:
            query['mileageTo'] = kwargs['mileage_to']

        # Power range
        if kwargs.get('power_from') is not None:
            query['powerFrom'] = kwargs['power_from']
        if kwargs.get('power_to') is not None:
            query['powerTo'] = kwargs['power_to']

        # Seller IDs
        if kwargs.get('seller_ids'):
            query['sellerIds'] = kwargs['seller_ids']

        # Excluding IDs
        if kwargs.get('excluding_ids'):
            query['excludingIds'] = kwargs['excluding_ids']

        # Location
        if kwargs.get('zip_code'):
            query['zipCode'] = kwargs['zip_code']
        if kwargs.get('radius_km'):
            query['radiusKm'] = kwargs['radius_km']

        # Boolean filters
        if kwargs.get('has_leasing_only') is not None:
            query['hasLeasingOnly'] = kwargs['has_leasing_only']
        if kwargs.get('has_warranty') is not None:
            query['hasWarranty'] = kwargs['has_warranty']
        if kwargs.get('has_accident_history') is not None:
            query['hasAccidentHistory'] = kwargs['has_accident_history']

        return query

    def search_listings(
        self,
        page: int = 0,
        size: int = 20,
        sort_type: Optional[SortType] = None,
        sort_order: Optional[SortOrder] = None,
        **search_params
    ) -> Dict[str, Any]:
        """
        Search for car listings.

        Args:
            page: Page number (0-indexed)
            size: Number of results per page (default 20, max usually 100)
            sort_type: Sort type (RELEVANCE, PRICE, MILEAGE, YEAR, POWER)
            sort_order: Sort order (ASC, DESC)
            **search_params: Additional search parameters (see SearchQuery dataclass)

        Returns:
            Dict containing search results with listings data

        Example:
            results = client.search_listings(
                make_key="bmw",
                model_key="x1",
                price_from=20000,
                price_to=50000,
                page=0,
                size=20
            )
        """
        url = f"{self.BASE_URL}/v1/listings/search"
        params = {"language": self.language}

        # Build request body
        query = self._build_search_query(**search_params)

        # Pagination
        pagination = {
            "page": page,
            "size": size
        }

        # Sort
        sort_list = []
        if sort_type or sort_order:
            sort_obj = {
                "type": (sort_type.value if isinstance(sort_type, SortType) else sort_type) or "RELEVANCE",
                "order": (sort_order.value if isinstance(sort_order, SortOrder) else sort_order) or "DESC",
                "variant": "v1"
            }
            sort_list.append(sort_obj)

        body = {
            "query": query,
            "pagination": pagination,
            "sort": sort_list
        }

        response = self.session.post(url, params=params, json=body)
        response.raise_for_status()

        data = response.json()

        # Normalize response: API returns "content" but we'll expose as "listings" for clarity
        if 'content' in data:
            data['listings'] = data.pop('content')

        return data

    def get_listing_details(self, listing_id: int) -> Dict[str, Any]:
        """
        Get detailed information about a specific listing.

        Note: The details are typically embedded in the page HTML rather than
        available through a dedicated API endpoint. This method navigates to
        the listing page URL.

        Args:
            listing_id: The listing ID

        Returns:
            Dict containing the listing URL

        Example:
            details = client.get_listing_details(20102453)
        """
        url = f"https://www.autoscout24.ch/{self.language}/d/{listing_id}"
        return {
            "listing_id": listing_id,
            "url": url,
            "note": "Details are embedded in the page HTML. Use a web scraper to extract them."
        }

    def get_quality_logos(self, seller_id: int) -> Dict[str, Any]:
        """
        Get quality logos/badges for a seller.

        Args:
            seller_id: The seller ID

        Returns:
            Dict containing quality logos data

        Example:
            logos = client.get_quality_logos(24860)
        """
        url = f"{self.BASE_URL}/v1/quali-logos/{seller_id}"
        params = {"language": self.language}

        response = self.session.get(url, params=params)
        response.raise_for_status()

        return response.json()

    def search_by_make(self, make: str, **kwargs) -> Dict[str, Any]:
        """
        Search listings by car make.

        Args:
            make: Car make (e.g., "bmw", "audi", "mercedes-benz")
            **kwargs: Additional search parameters

        Returns:
            Dict containing search results

        Example:
            results = client.search_by_make("bmw", price_to=50000)
        """
        return self.search_listings(make_key=make, **kwargs)

    def search_electric_cars(self, **kwargs) -> Dict[str, Any]:
        """
        Search for electric vehicles.

        Args:
            **kwargs: Additional search parameters

        Returns:
            Dict containing search results

        Example:
            results = client.search_electric_cars(price_to=60000)
        """
        return self.search_listings(fuel_type=FuelType.ELECTRIC, **kwargs)

    def search_new_cars(self, **kwargs) -> Dict[str, Any]:
        """
        Search for new vehicles.

        Args:
            **kwargs: Additional search parameters

        Returns:
            Dict containing search results

        Example:
            results = client.search_new_cars(make_key="tesla")
        """
        return self.search_listings(condition_type=ConditionType.NEW, **kwargs)


def main():
    """Example usage of the AutoScout24 client"""

    # Initialize client
    client = AutoScout24Client(language="de")

    print("=" * 80)
    print("AutoScout24.ch API Client - Example Usage")
    print("=" * 80)

    # Example 1: Search for BMW X1 vehicles
    print("\n1. Searching for BMW X1 vehicles between CHF 20,000 and CHF 50,000...")
    results = client.search_listings(
        make_key="bmw",
        model_key="x1",
        price_from=20000,
        price_to=50000,
        vehicle_categories=[VehicleCategory.CAR],
        page=0,
        size=5
    )

    print(f"   Found {results.get('totalResultCount', 0)} results")
    print(f"   Showing first {len(results.get('listings', []))} listings:\n")

    for i, listing in enumerate(results.get('listings', [])[:5], 1):
        make_name = listing.get('make', {}).get('name', 'Unknown')
        model_name = listing.get('model', {}).get('name', 'Unknown')
        version = listing.get('versionFullName', '')
        title = f"{make_name} {model_name} {version}".strip()

        print(f"   {i}. {title}")
        print(f"      Price: CHF {listing.get('price', 'N/A'):,}")
        print(f"      Year: {listing.get('firstRegistrationYear', 'N/A')}")
        print(f"      Mileage: {listing.get('mileage', 'N/A'):,} km")
        print(f"      Power: {listing.get('horsePower', 'N/A')} HP")
        print(f"      ID: {listing.get('id', 'N/A')}")
        print()

    # Example 2: Search for electric vehicles
    print("\n2. Searching for electric vehicles...")
    ev_results = client.search_electric_cars(
        price_to=60000,
        page=0,
        size=3
    )

    print(f"   Found {ev_results.get('totalResultCount', 0)} electric vehicles")
    print(f"   Showing first {len(ev_results.get('listings', []))} listings:\n")

    for i, listing in enumerate(ev_results.get('listings', [])[:3], 1):
        make_name = listing.get('make', {}).get('name', 'Unknown')
        model_name = listing.get('model', {}).get('name', 'Unknown')
        title = f"{make_name} {model_name}".strip()

        print(f"   {i}. {title}")
        print(f"      Price: CHF {listing.get('price', 'N/A'):,}")
        print(f"      Year: {listing.get('firstRegistrationYear', 'N/A')}")
        print()

    # Example 3: Search with multiple filters
    print("\n3. Searching for used SUVs with warranty...")
    suv_results = client.search_listings(
        body_type=BodyType.SUV,
        condition_type=ConditionType.USED,
        has_warranty=True,
        price_from=15000,
        price_to=40000,
        year_from=2020,
        page=0,
        size=3
    )

    print(f"   Found {suv_results.get('totalResultCount', 0)} results")
    print(f"   Showing first {len(suv_results.get('listings', []))} listings:\n")

    for i, listing in enumerate(suv_results.get('listings', [])[:3], 1):
        make_name = listing.get('make', {}).get('name', 'Unknown')
        model_name = listing.get('model', {}).get('name', 'Unknown')
        title = f"{make_name} {model_name}".strip()

        print(f"   {i}. {title}")
        print(f"      Price: CHF {listing.get('price', 'N/A'):,}")
        print(f"      Year: {listing.get('firstRegistrationYear', 'N/A')}")
        print()

    # Example 4: Get quality logos for a seller
    print("\n4. Getting quality logos for seller ID 24860...")
    try:
        logos = client.get_quality_logos(24860)
        print(f"   Quality logos retrieved: {len(logos.get('logos', []))} logos found")
    except Exception as e:
        print(f"   Error: {e}")

    print("\n" + "=" * 80)
    print("Examples completed!")
    print("=" * 80)


if __name__ == "__main__":
    main()
