# generated by datamodel-codegen:
#   filename:  core/targeting.json
#   timestamp: 2026-02-28T17:39:50+00:00

from __future__ import annotations

from enum import Enum
from typing import Annotated, Any

from adcp.types.base import AdCPBaseModel
from pydantic import ConfigDict, Field, RootModel

from ..enums import age_verification_method
from ..enums import device_platform as device_platform_1
from ..enums import device_type as device_type_1
from ..enums import distance_unit, metro_system, postal_system
from ..enums import transport_mode as transport_mode_1
from . import daypart_target
from . import ext as ext_1
from . import frequency_cap as frequency_cap_1
from . import property_list_ref


class AgeRestriction(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    accepted_methods: Annotated[
        list[age_verification_method.AgeVerificationMethod] | None,
        Field(
            description='Accepted verification methods. If omitted, any method the platform supports is acceptable.',
            min_length=1,
        ),
    ] = None
    min: Annotated[int, Field(description='Minimum age required', ge=13, le=99)]
    verification_required: Annotated[
        bool | None,
        Field(description='Whether verified age (not inferred) is required for compliance'),
    ] = False


class GeoCountry(RootModel[str]):
    root: Annotated[str, Field(pattern='^[A-Z]{2}$')]


class GeoCountriesExcludeItem(GeoCountry):
    pass


class GeoMetro(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    system: Annotated[
        metro_system.MetroAreaSystem,
        Field(description="Metro area classification system (e.g., 'nielsen_dma', 'uk_itl2')"),
    ]
    values: Annotated[
        list[str],
        Field(
            description="Metro codes within the system (e.g., ['501', '602'] for Nielsen DMAs)",
            min_length=1,
        ),
    ]


class GeoMetrosExcludeItem(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    system: Annotated[
        metro_system.MetroAreaSystem,
        Field(description="Metro area classification system (e.g., 'nielsen_dma', 'uk_itl2')"),
    ]
    values: Annotated[
        list[str],
        Field(
            description="Metro codes to exclude within the system (e.g., ['501', '602'] for Nielsen DMAs)",
            min_length=1,
        ),
    ]


class GeoPostalArea(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    system: Annotated[
        postal_system.PostalCodeSystem,
        Field(
            description="Postal code system (e.g., 'us_zip', 'gb_outward'). System name encodes country and precision."
        ),
    ]
    values: Annotated[
        list[str],
        Field(
            description="Postal codes within the system (e.g., ['10001', '10002'] for us_zip)",
            min_length=1,
        ),
    ]


class GeoPostalAreasExcludeItem(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    system: Annotated[
        postal_system.PostalCodeSystem,
        Field(
            description="Postal code system (e.g., 'us_zip', 'gb_outward'). System name encodes country and precision."
        ),
    ]
    values: Annotated[
        list[str],
        Field(
            description="Postal codes to exclude within the system (e.g., ['10001', '10002'] for us_zip)",
            min_length=1,
        ),
    ]


class Type(Enum):
    Polygon = 'Polygon'
    MultiPolygon = 'MultiPolygon'


class Geometry(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    coordinates: Annotated[
        list[Any],
        Field(
            description='GeoJSON coordinates array. For Polygon: array of linear rings. For MultiPolygon: array of polygons.'
        ),
    ]
    type: Annotated[Type, Field(description='GeoJSON geometry type.')]


class Radius(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    unit: Annotated[distance_unit.DistanceUnit, Field(description='Distance unit.')]
    value: Annotated[float, Field(description='Radius distance.', gt=0.0)]


class Unit(Enum):
    min = 'min'
    hr = 'hr'


class TravelTime(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    unit: Annotated[Unit, Field(description='Time unit.')]
    value: Annotated[float, Field(description='Travel time limit.', ge=1.0)]


class Geometry4(Geometry):
    pass


class TravelTime4(TravelTime):
    pass


class Geometry5(Geometry):
    pass


class TravelTime5(TravelTime):
    pass


class GeoRegion(RootModel[str]):
    root: Annotated[str, Field(pattern='^[A-Z]{2}-[A-Z0-9]{1,3}$')]


class GeoRegionsExcludeItem(GeoRegion):
    pass


class MatchType(Enum):
    broad = 'broad'
    phrase = 'phrase'
    exact = 'exact'


class KeywordTarget(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    bid_price: Annotated[
        float | None,
        Field(
            description="Per-keyword bid price, denominated in the same currency as the package's pricing option. Overrides the package-level bid_price for this keyword. Inherits the max_bid interpretation from the pricing option: when max_bid is true, this is the keyword's bid ceiling; when false, this is the exact bid. If omitted, the package bid_price applies.",
            ge=0.0,
        ),
    ] = None
    keyword: Annotated[str, Field(description='The keyword to target', min_length=1)]
    match_type: Annotated[
        MatchType,
        Field(
            description='Match type: broad matches related queries, phrase matches queries containing the keyword phrase, exact matches the query exactly'
        ),
    ]


class LanguageItem(RootModel[str]):
    root: Annotated[str, Field(pattern='^[a-z]{2}$')]


class NegativeKeyword(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    keyword: Annotated[str, Field(description='The keyword to exclude', min_length=1)]
    match_type: Annotated[MatchType, Field(description='Match type for exclusion')]


class StoreCatchment(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    catalog_id: Annotated[
        str, Field(description='Synced store-type catalog ID from sync_catalogs.')
    ]
    catchment_ids: Annotated[
        list[str] | None,
        Field(
            description="Catchment zone IDs to target (e.g., 'walk', 'drive'). Omit to target all catchment zones.",
            min_length=1,
        ),
    ] = None
    store_ids: Annotated[
        list[str] | None,
        Field(
            description='Filter to specific stores within the catalog. Omit to target all stores.',
            min_length=1,
        ),
    ] = None


class GeoProximity(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    ext: ext_1.ExtensionObject | None = None
    geometry: Annotated[
        Geometry | None,
        Field(
            description='Pre-computed GeoJSON geometry defining the proximity boundary. Use when the buyer has already calculated isochrones (via TravelTime, Mapbox, etc.) or has custom boundaries. When geometry is provided, lat/lng are not required.'
        ),
    ] = None
    label: Annotated[
        str | None,
        Field(
            description="Human-readable label for this entry (e.g., 'Düsseldorf', 'Heathrow Airport', 'Primary trade area')."
        ),
    ] = None
    lat: Annotated[
        float,
        Field(
            description='Latitude in decimal degrees (WGS 84). Required for travel_time and radius methods.',
            ge=-90.0,
            le=90.0,
        ),
    ]
    lng: Annotated[
        float,
        Field(
            description='Longitude in decimal degrees (WGS 84). Required for travel_time and radius methods.',
            ge=-180.0,
            le=180.0,
        ),
    ]
    radius: Annotated[
        Radius | None,
        Field(
            description='Simple radius from the point. The platform draws a circle of this distance around the coordinates.'
        ),
    ] = None
    transport_mode: Annotated[
        transport_mode_1.TransportMode,
        Field(
            description='Transportation mode for isochrone calculation. Required when travel_time is provided.'
        ),
    ]
    travel_time: Annotated[
        TravelTime,
        Field(
            description='Travel time limit for isochrone calculation. The platform resolves this to a geographic boundary based on actual transportation networks.'
        ),
    ]


class GeoProximity2(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    ext: ext_1.ExtensionObject | None = None
    geometry: Annotated[
        Geometry4 | None,
        Field(
            description='Pre-computed GeoJSON geometry defining the proximity boundary. Use when the buyer has already calculated isochrones (via TravelTime, Mapbox, etc.) or has custom boundaries. When geometry is provided, lat/lng are not required.'
        ),
    ] = None
    label: Annotated[
        str | None,
        Field(
            description="Human-readable label for this entry (e.g., 'Düsseldorf', 'Heathrow Airport', 'Primary trade area')."
        ),
    ] = None
    lat: Annotated[
        float,
        Field(
            description='Latitude in decimal degrees (WGS 84). Required for travel_time and radius methods.',
            ge=-90.0,
            le=90.0,
        ),
    ]
    lng: Annotated[
        float,
        Field(
            description='Longitude in decimal degrees (WGS 84). Required for travel_time and radius methods.',
            ge=-180.0,
            le=180.0,
        ),
    ]
    radius: Annotated[
        Radius,
        Field(
            description='Simple radius from the point. The platform draws a circle of this distance around the coordinates.'
        ),
    ]
    transport_mode: Annotated[
        transport_mode_1.TransportMode | None,
        Field(
            description='Transportation mode for isochrone calculation. Required when travel_time is provided.'
        ),
    ] = None
    travel_time: Annotated[
        TravelTime4 | None,
        Field(
            description='Travel time limit for isochrone calculation. The platform resolves this to a geographic boundary based on actual transportation networks.'
        ),
    ] = None


class GeoProximity3(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    ext: ext_1.ExtensionObject | None = None
    geometry: Annotated[
        Geometry5,
        Field(
            description='Pre-computed GeoJSON geometry defining the proximity boundary. Use when the buyer has already calculated isochrones (via TravelTime, Mapbox, etc.) or has custom boundaries. When geometry is provided, lat/lng are not required.'
        ),
    ]
    label: Annotated[
        str | None,
        Field(
            description="Human-readable label for this entry (e.g., 'Düsseldorf', 'Heathrow Airport', 'Primary trade area')."
        ),
    ] = None
    lat: Annotated[
        float | None,
        Field(
            description='Latitude in decimal degrees (WGS 84). Required for travel_time and radius methods.',
            ge=-90.0,
            le=90.0,
        ),
    ] = None
    lng: Annotated[
        float | None,
        Field(
            description='Longitude in decimal degrees (WGS 84). Required for travel_time and radius methods.',
            ge=-180.0,
            le=180.0,
        ),
    ] = None
    radius: Annotated[
        Radius | None,
        Field(
            description='Simple radius from the point. The platform draws a circle of this distance around the coordinates.'
        ),
    ] = None
    transport_mode: Annotated[
        transport_mode_1.TransportMode | None,
        Field(
            description='Transportation mode for isochrone calculation. Required when travel_time is provided.'
        ),
    ] = None
    travel_time: Annotated[
        TravelTime5 | None,
        Field(
            description='Travel time limit for isochrone calculation. The platform resolves this to a geographic boundary based on actual transportation networks.'
        ),
    ] = None


class TargetingOverlay(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    age_restriction: Annotated[
        AgeRestriction | None,
        Field(
            description='Age restriction for compliance. Use for legal requirements (alcohol, gambling), not audience targeting.'
        ),
    ] = None
    audience_exclude: Annotated[
        list[str] | None,
        Field(
            description='Suppress delivery to members of these first-party CRM audiences. Matched users are excluded regardless of other targeting. References audience_id values from sync_audiences on the same seller account — audience IDs are not portable across sellers. Seller must declare support in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    audience_include: Annotated[
        list[str] | None,
        Field(
            description='Restrict delivery to members of these first-party CRM audiences. Only users present in the uploaded lists are eligible. References audience_id values from sync_audiences on the same seller account — audience IDs are not portable across sellers. Not for lookalike expansion — express that intent in the campaign brief. Seller must declare support in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    axe_exclude_segment: Annotated[
        str | None, Field(description='AXE segment ID to exclude from targeting')
    ] = None
    axe_include_segment: Annotated[
        str | None, Field(description='AXE segment ID to include for targeting')
    ] = None
    daypart_targets: Annotated[
        list[daypart_target.DaypartTarget] | None,
        Field(
            description='Restrict delivery to specific time windows. Each entry specifies days of week and an hour range.',
            min_length=1,
        ),
    ] = None
    device_platform: Annotated[
        list[device_platform_1.DevicePlatform] | None,
        Field(
            description='Restrict to specific platforms. Use for technical compatibility (app only works on iOS). Values from Sec-CH-UA-Platform standard, extended for CTV.',
            min_length=1,
        ),
    ] = None
    device_type: Annotated[
        list[device_type_1.DeviceType] | None,
        Field(
            description='Restrict to specific device form factors. Use for campaigns targeting hardware categories rather than operating systems (e.g., mobile-only promotions, CTV campaigns).',
            min_length=1,
        ),
    ] = None
    device_type_exclude: Annotated[
        list[device_type_1.DeviceType] | None,
        Field(
            description='Exclude specific device form factors from delivery (e.g., exclude CTV for app-install campaigns).',
            min_length=1,
        ),
    ] = None
    frequency_cap: frequency_cap_1.FrequencyCap | None = None
    geo_countries: Annotated[
        list[GeoCountry] | None,
        Field(
            description="Restrict delivery to specific countries. ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE').",
            min_length=1,
        ),
    ] = None
    geo_countries_exclude: Annotated[
        list[GeoCountriesExcludeItem] | None,
        Field(
            description="Exclude specific countries from delivery. ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE').",
            min_length=1,
        ),
    ] = None
    geo_metros: Annotated[
        list[GeoMetro] | None,
        Field(
            description='Restrict delivery to specific metro areas. Each entry specifies the classification system and target values. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_metros_exclude: Annotated[
        list[GeoMetrosExcludeItem] | None,
        Field(
            description='Exclude specific metro areas from delivery. Each entry specifies the classification system and excluded values. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_postal_areas: Annotated[
        list[GeoPostalArea] | None,
        Field(
            description='Restrict delivery to specific postal areas. Each entry specifies the postal system and target values. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_postal_areas_exclude: Annotated[
        list[GeoPostalAreasExcludeItem] | None,
        Field(
            description='Exclude specific postal areas from delivery. Each entry specifies the postal system and excluded values. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_proximity: Annotated[
        list[GeoProximity | GeoProximity2 | GeoProximity3] | None,
        Field(
            description='Target users within travel time, distance, or a custom boundary around arbitrary geographic points. Multiple entries use OR semantics — a user within range of any listed point is eligible. For campaigns targeting 10+ locations, consider using store_catchments with a location catalog instead. Seller must declare support in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_regions: Annotated[
        list[GeoRegion] | None,
        Field(
            description="Restrict delivery to specific regions/states. ISO 3166-2 subdivision codes (e.g., 'US-CA', 'GB-SCT').",
            min_length=1,
        ),
    ] = None
    geo_regions_exclude: Annotated[
        list[GeoRegionsExcludeItem] | None,
        Field(
            description="Exclude specific regions/states from delivery. ISO 3166-2 subdivision codes (e.g., 'US-CA', 'GB-SCT').",
            min_length=1,
        ),
    ] = None
    keyword_targets: Annotated[
        list[KeywordTarget] | None,
        Field(
            description='Keyword targeting for search and retail media platforms. Restricts delivery to queries matching the specified keywords. Each keyword is identified by the tuple (keyword, match_type) — the same keyword string with different match types are distinct targets. Sellers SHOULD reject duplicate (keyword, match_type) pairs within a single request. Seller must declare support in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    language: Annotated[
        list[LanguageItem] | None,
        Field(
            description="Restrict to users with specific language preferences. ISO 639-1 codes (e.g., 'en', 'es', 'fr').",
            min_length=1,
        ),
    ] = None
    negative_keywords: Annotated[
        list[NegativeKeyword] | None,
        Field(
            description='Keywords to exclude from delivery. Queries matching these keywords will not trigger the ad. Each negative keyword is identified by the tuple (keyword, match_type). Seller must declare support in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    property_list: Annotated[
        property_list_ref.PropertyListReference | None,
        Field(
            description="Reference to a property list for targeting specific properties within this product. The package runs on the intersection of the product's publisher_properties and this list. Sellers SHOULD return a validation error if the product has property_targeting_allowed: false."
        ),
    ] = None
    store_catchments: Annotated[
        list[StoreCatchment] | None,
        Field(
            description='Target users within store catchment areas from a synced store catalog. Each entry references a store-type catalog and optionally narrows to specific stores or catchment zones.',
            min_length=1,
        ),
    ] = None
