import L, { LatLngExpression, Map, LatLngBounds } from 'leaflet';

interface Pictogram {
    /**
     * A unique identifier for the pictogram.
     * This ID is provided by the underlying data source (e.g., Global Symbols API).
     */
    id: string;
    /**
     * URL of the pictogram.
     */
    url: string;
    /**
     * Text to display below the pictogram.
     */
    displayText: string;
    /**
     * ARIA label for the pictogram.
     */
    ariaLabel?: string;
    /**
     * Description of the pictogram.
     */
    description?: string;
    /**
     * A flexible container for any additional metadata from the data source.
     */
    metadata?: Record<string, any>;
}

/**
 * Represents a single point of interest (POI).
 *
 * A Point of Interest (POI) is a location that has specific significance,
 * such as a restaurant, park, store, landmark, or other notable place.
 * Each POI contains key information such as its name, type, and geographic location.
 */
interface PointOfInterest {
    /**
     * A unique identifier for the point of interest.
     * This ID is provided by the underlying data source (e.g., Google Places, Overpass API).
     */
    id: string;
    /**
     * The human-readable name of the point of interest.
     * @example "Cafe Mozart", "Schönbrunn Palace"
     */
    name: string;
    /**
     * The category or type of the point of interest.
     * @example "restaurant", "park", "museum"
     */
    type: string;
    /**
     * The latitude coordinate of the point of interest.
     */
    latitude: number;
    /**
     * The longitude coordinate of the point of interest.
     */
    longitude: number;
    /**
     * An optional address or description of the point of interest.
     * @example "Albertinaplatz 2, 1010 Wien, Austria"
     */
    address?: string;
    /**
     * A flexible container for any additional metadata from the data source.
     * E.g. operational hours, tags, or other details.
     */
    metadata?: Record<string, any>;
}

interface PictogramMarkerOptions {
    /**
     * Whether to add the pictogram description to the pictogram marker in case a description is available.
     *
     * @default false
     */
    addDescription?: boolean;
    /**
     * Whether to bring the pictogram marker to the front when clicked.
     *
     * @default true
     */
    bringToFrontOnClick?: boolean;
    /**
     * Whether to bring the pictogram marker to the front when hovered.
     *
     * @default true
     */
    bringToFrontOnHover?: boolean;
    /**
     * Whether to bring the pictogram marker to the front when focused.
     *
     * @default true
     */
    bringToFrontOnFocus?: boolean;
    /**
     * A callback function that is called when the pictogram marker is clicked.
     *
     * @default undefined
     */
    onClick?: (pictogram: Pictogram, pointOfInterest?: PointOfInterest) => void;
}
/**
 * A custom Leaflet layer that displays a pictogram marker at a specified geographical location.
 *
 * This marker supports accessibility features, customizable interactions, and flexible styling.
 * It is designed to be used with the Leaflet library and integrates easily into any Leaflet map.
 */
declare class PictogramMarker extends L.Layer {
    private readonly addDescription;
    private readonly bringToFrontOnClick;
    private readonly bringToFrontOnHover;
    private readonly bringToFrontOnFocus;
    private readonly onClick;
    private readonly _latlng;
    private readonly _pictogram;
    private readonly _pointOfInterest;
    private container?;
    private box?;
    private map;
    getLatLng(): L.LatLng;
    /**
     * Constructs a new instance of the `PictogramMarker` class.
     *
     * @param latlng - The geographical coordinates where the marker should be placed.
     * @param pictogram - The pictogram object containing the display data (e.g., URL, label, description).
     * @param pointOfInterest - (Optional) The associated point of interest for this marker.
     * @param options - (Optional) Configuration options for the marker's behavior and interactivity.
     *
     * @example
     * ```typescript
     * const latlng = L.latLng(48.20849, 16.37208);
     * const pictogram = {
     *     id: '1',
     *     url: 'https://example.com/pictogram.png',
     *     displayText: 'Restaurant',
     *     description: 'A fine dining restaurant serving local cuisine.'
     * };
     *
     * const options = {
     *     addDescription: true,
     *     bringToFrontOnClick: true,
     *     onClick: (pictogram) => {
     *         console.log('Pictogram clicked:', pictogram);
     *     }
     * };
     *
     * const marker = new PictogramMarker(latlng, pictogram, undefined, options);
     * marker.addTo(map);
     * ```
     */
    constructor(latlng: LatLngExpression, pictogram: Pictogram, pointOfInterest?: PointOfInterest, options?: PictogramMarkerOptions);
    onAdd(map: Map): this;
    onRemove(map: Map): this;
    private updatePosition;
    private setupInteractions;
    private toggleInFront;
}

/**
 * Options for querying points of interest within a specified geographic area.
 *
 * Implementations should adhere to the following behaviors:
 * - If the `types` array is not provided, the query should include all available types.
 * - If the `types` array is provided but is empty, the query should exclude all results (no types match).
 * - The `limit` specifies the maximum number of results and should be respected when provided.
 * - The `metadata` field allows for additional, implementation-specific query parameters.
 */
interface PointOfInterestQueryOptions {
    /**
     * A list of types or categories to filter the results.
     * - If this field is omitted, the query should include all available types.
     * - If this field is provided as an empty array, the query should return no results.
     * Examples: ["restaurant", "park", "museum"].
     */
    types?: string[];
    /**
     * The maximum number of results to return.
     * If omitted, the implementation should return all results or the default maximum limit.
     * Example: 50.
     */
    limit?: number;
    /**
     * A flexible container for any additional options specific to the implementation.
     * This can include fields such as `openNow` (only return currently open places), `radius`, or API-specific flags.
     */
    metadata?: Record<string, any>;
}

/**
 * Abstract interface for a service that fetches points of interest (POIs)
 * within a specified geographic area.
 *
 * Implementations of this interface should:
 * - Query a data source (e.g., Google Places API, Overpass API, or Apple Maps Server API).
 * - Adhere to the filtering and behavior rules defined in `PointOfInterestQueryOptions`.
 * - Return a list of POIs with standardized fields as defined in `PointOfInterest`.
 */
interface PointOfInterestService {
    /**
     * Fetches a list of points of interest within the given bounds.
     *
     * @param bounds - The geographic bounds for the query. Specifies the area to search for POIs.
     * @param options - Optional filters and query settings.
     * - If `types` is omitted, all available POI types should be included in the query.
     * - If `types` is an empty array, no POIs should be returned.
     * - If `limit` is specified, the implementation should respect this value.
     * @returns A promise resolving to an array of POIs.
     */
    getPointsOfInterest(bounds: LatLngBounds, options?: PointOfInterestQueryOptions): Promise<PointOfInterest[]>;
}

/**
 * Options for configuring the OverpassPOIService.
 */
interface OverpassPOIServiceOptions {
    /**
     * The base URL of the Overpass API endpoint.
     *
     * @default "https://overpass-api.de/api/interpreter".
     * @see https://wiki.openstreetmap.org/wiki/Overpass_API#Public_Overpass_API_instances Public Overpass API instances
     */
    apiUrl?: string;
    /**
     * Default types of points of interest to query if none are provided in the {@link PointOfInterestQueryOptions}
     * object in the {@link getPointsOfInterest} method.
     *
     * @default ["shop", "leisure"]
     * @see https://wiki.openstreetmap.org/wiki/Key:shop
     * @see https://wiki.openstreetmap.org/wiki/Key:leisure
     */
    defaultTypes?: string[];
    /**
     * OpenStreetMap types to query.
     * Possible values: "node", "way", "relation".
     *
     * @default ["node"]
     * @see https://wiki.openstreetmap.org/wiki/Elements
     */
    osmTypes?: string[];
    /**
     * Default number of points of interest to query if no limit is provided in the {@link PointOfInterestQueryOptions}
     * object in the {@link getPointsOfInterest} method.
     *
     * @default 25
     */
    defaultLimit?: number;
    /**
     * Maximum number of retries for requests in case of rate limiting or server errors.
     *
     * @default 3
     */
    maxRetries?: number;
    /**
     * Timeout between retries in milliseconds.
     *
     * @default 1000
     */
    retryDelay?: number;
    /**
     * Timeout for a single request in seconds.
     *
     * @default 25
     */
    timeout?: number;
    /**
     * Whether to try to derive names for POIs with no name from their type.
     *
     * Some POIs may not have a name, but they have a type (e.g., "restaurant"). If this option is enabled,
     * the service will try to derive a generic name for the {@link PointOfInterest} from the type (e.g., "Restaurant").
     *
     * @default true
     */
    deriveNames?: boolean;
    /**
     * Whether to filter out POIs with no name.
     *
     * If this option is enabled, POIs with no name will be filtered out from the results.
     * Otherwise, POIs with no name will be included in the results and will have the name "Unknown".
     *
     * @default true
     */
    filterOutNoName?: boolean;
}

/**
 * Abstract interface for a service that provides a {@link Pictogram} for a given  {@link PointOfInterest}.
 */
interface PictogramService {
    /**
     * @description Fetches a pictogram for the given point of interest.
     *
     * @param poi The {@link PointOfInterest} to fetch a pictogram for.
     * @returns A promise resolving to a {@link Pictogram} object or `undefined` if no pictogram is found.
     */
    getPictogram(poi: PointOfInterest): Promise<Pictogram | undefined>;
}

/**
 * @description Options for configuring the GlobalSymbolsPictogramService.
 * @see https://globalsymbols.com/api/docs Global Symbols API documentation
 */
interface GlobalSymbolsPictogramServiceOptions {
    /**
     * @description The base URL of the Global Symbols API endpoint.
     * @default "https://globalsymbols.com/api/v1/concepts/suggest".
     */
    apiUrl?: string;
    /**
     * @description The symbol set the pictogram should be fetched from.
     * @default "arasaac"
     * @remarks Examples: `"arasaac"`, `"sclera"`, `"blissymbols"`, etc.
     * @see https://globalsymbols.com/api/docs Global Symbols API documentation
     * @see https://globalsymbols.com/api/v1/symbolsets GET all available symbol sets
     */
    symbolSet?: string;
    /**
     * @description Include the type of the point of interest in the display text of the pictogram.
     * @default false
     */
    includeTypeInDisplayText?: boolean;
    /**
     * @description Include the type of the point of interest in the aria label of the pictogram.
     * @default true
     */
    includeTypeInAriaLabel?: boolean;
    /**
     * @description Cache strategy: "in-memory" (in-memory caching) or "local-storage" (persistent caching).
     * @default "local-storage"
     */
    cacheStrategy?: "in-memory" | "local-storage";
    /**
     * @description Cache expiration time in milliseconds.
     * @default 604800000 (1 week)
     */
    cacheExpiration?: number;
    /**
     * @description Cache prefix for the local storage cache.
     * @default "global-symbols-pictogram-service"
     * @remarks This is used to avoid conflicts with other local storage items.
     */
    cachePrefix?: string;
}

/**
 * Abstract interface for a service that sorts markers on a map.
 *
 * The sorting order does not affect the visual position of the markers on the map, but rather the order in which they
 * are added to the DOM. This is relevant for screen readers and keyboard navigation.
 */
interface MarkerSortingService {
    /**
     * Sorts the given markers but DOES NOT add them to the map.
     *
     * @param markers The markers to sort.
     * @param map The map is provided for context, e.g. to determine the current map bounds, mapping the coordinates
     * to layer points, etc.
     *
     * @returns A promise resolving to the sorted markers.
     */
    sortMarkers(markers: PictogramMarker[], map: L.Map): Promise<PictogramMarker[]>;
}

interface GridSortingServiceOptions {
    /**
     * The layout direction for the x-axis: "lr" (left-to-right) or "rl" (right-to-left).
     *
     * @default "lr"
     */
    lr: "lr" | "rl";
    /**
     * The layout direction for the y-axis: "tb" (top-to-bottom) or "bt" (bottom-to-top).
     *
     * @default "tb"
     */
    tb: "tb" | "bt";
    /**
     * The threshold in pixels to determine row separation.
     *
     * @default 64
     */
    rowThreshold: number;
}

/**
 * Options for configuring the Independo Maps plugin.
 */
interface IndependoMapsOptions {
    /**
     * Options for the {@link PictogramMarker}s created by the plugin.
     */
    pictogramMarkerOptions?: PictogramMarkerOptions;
    /**
     * Options for configuring the default {@link OverpassPOIService}.
     *
     * These options are used if no custom `poiService` is provided.
     * Allows customization of the Overpass API endpoint, default types, and other service-specific behaviors.
     *
     * @example
     * ```typescript
     * overpassServiceOptions: {
     *   apiUrl: "https://custom-overpass-api.com",
     *   defaultLimit: 50,
     *   defaultTypes: ["amenity", "shop", "tourism"]
     * };
     * ```
     */
    overpassServiceOptions?: OverpassPOIServiceOptions;
    /**
     * Options for configuring the default {@link GlobalSymbolsPictogramService}.
     *
     * These options are used if no custom `pictogramService` is provided.
     * Allows customization of behavior such as including types in display text and symbol set selection.
     *
     * @example
     * ```typescript
     * globalSymbolsServiceOptions: {
     *   includeTypeInDisplayText: true,
     *   symbolSet: "your-custom-symbolset"
     * };
     * ```
     */
    globalSymbolsServiceOptions?: GlobalSymbolsPictogramServiceOptions;
    /**
     * Options for configuring the default {@link GridSortingService}.
     *
     * @remarks This option is used to define the layout direction of the pictograms when adding them to the DOM.
     * This is relevant for screen readers and keyboard navigation. On the
     * @example {lr: "lr", tb: "tb"}
     */
    gridSortServiceOptions?: GridSortingServiceOptions;
    /**
     * Custom implementation of the {@link PointOfInterestService}.
     *
     * Use this field to provide your own service for fetching points of interest (POIs).
     * If not provided, the plugin will default to an instance of {@link OverpassPOIService}.
     *
     * @example
     * ```typescript
     * poiService: new CustomPOIService();
     * ```
     */
    poiService?: PointOfInterestService;
    /**
     * Custom implementation of the {@link PictogramService}.
     *
     * Use this field to provide your own service for fetching pictograms for POIs.
     * If not provided, the plugin will default to an instance of {@link GlobalSymbolsPictogramService}.
     *
     * @example
     * ```typescript
     * pictogramService: new CustomPictogramService();
     * ```
     */
    pictogramService?: PictogramService;
    /**
     * Custom implementation of the {@link MarkerSortingService}.
     *
     * Use this field to provide your own service for sorting markers on the map.
     * If not provided, the plugin will default to an instance of {@link GridSortingService}.
     *
     * @remarks The sorting order does not affect the visual position of the markers on the map, but rather the order in
     * which they are added to the DOM. This is relevant for screen readers and keyboard navigation. One can imagine
     * use cases where the markers should be sorted in a specific order, e.g. by distance to the user's location.
     */
    markerSortingService?: MarkerSortingService;
    /**
     * Debounce interval in milliseconds for updating the map after a move or zoom event.
     *
     * @remarks This interval prevents the map from updating too frequently and causing performance issues.
     * @default 300
     */
    debounceInterval?: number;
    /**
     * The default pictogram to use when no pictogram is found for a POI.
     *
     * @remarks This pictogram will be used when the pictogram service returns `undefined` for a POI. If no pictogram
     * can be found for a POI and the default pictogram is not provided, the POI will not be displayed on the map.
     * @default undefined
     */
    defaultPictogram?: Pictogram;
}
declare class IndependoMaps {
    private readonly debounceInterval;
    private readonly defaultPictogram?;
    private readonly map;
    private readonly poiLayerGroup;
    private readonly poiService;
    private readonly pictogramService;
    private readonly markerSortingService;
    private readonly pictogramMarkerOptions?;
    constructor(map: L.Map, options?: IndependoMapsOptions);
    /**
     * Updates the map by fetching POIs and adding corresponding markers.
     */
    private updateMap;
}
/**
 * Initializes the Independo Maps plugin on a {@link L.Map} and returns an instance of the plugin.
 *
 * @param map The {@link L.Map} to initialize the plugin on.
 * @param options Optional {@link IndependoMapsOptions} to configure the plugin.
 * @returns An instance of {@link IndependoMaps}.
 */
declare function initIndependoMaps(map: L.Map, options?: IndependoMapsOptions): IndependoMaps;

export { PictogramMarker, initIndependoMaps };
