export interface MapplsResponse {provider:string;attribution:string;data:Record<string,any>;traffic?:boolean}
export interface MapplsConfig {provider:string;configured:boolean;services:string[];basemaps:Array<{id:string;name:string;labels:boolean}>;note:string}
export interface MapplsRouteOptions {from_lat:number;from_lon:number;to_lat:number;to_lon:number;profile?:'driving'|'walking'|'biking';region?:string}
export interface MapplsAdapter {
 mapplsConfig():Promise<MapplsConfig>;
 mapplsSearch(options:{q:string;lat?:number;lon?:number;region?:string}):Promise<MapplsResponse>;
 mapplsNearby(options:{q:string;lat:number;lon:number;radius?:number;page?:number}):Promise<MapplsResponse>;
 mapplsReverse(options:{lat:number;lon:number}):Promise<MapplsResponse>;
 mapplsPlace(pin:string):Promise<MapplsResponse>;
 mapplsRoute(options:MapplsRouteOptions):Promise<MapplsResponse>;
}

export interface PlaceInput {id:string;name:string;lat:number;lon:number;modelAssets?:ModelAsset[];richContent?:RichContent;[attribute:string]:unknown}
export interface RevisionOptions {expectedVersion:number;note?:string}
export interface Contribution {id:string;scenario:string;place_id:string;author:string;operation:'create'|'update'|'delete';status:'pending'|'published'|'rejected';base_version:number;applied_version:number|null;payload:PlaceInput|null;previous:{payload:PlaceInput;deleted:number}|null;note:string;submitted_note:string;created:number;reviewer:string|null;reviewed:number|null}
export interface ContributionFilter {scenario?:string;status?:'awaiting'|'pending'|'published'|'rejected'|'';offset?:number}
export interface ModelAsset {id:string;model?:string;mediaId?:string;name?:string;lat?:number;lon?:number;heading?:number;pitch?:number;roll?:number;scale?:[number,number,number];altitudeOffset?:number;tint?:string;enabled?:boolean;group?:'exterior'|'interior'|'roof';provenance?:string}
export interface RichMedia {id:string;type:'image'|'video'|'audio'|'link';mediaId?:string;url?:string;title?:string;caption?:string;alt?:string;attribution?:string;licence?:string}
export interface RichContent {sections:Array<{title:string;body:string}>;media:RichMedia[]}
export interface MediaUpload {id:string;name:string;mime:string;bytes:number;uri:string}
export interface MediaLibrary {models:Array<{id:string;name:string;uri:string;bytes:number}>;uploads:MediaUpload[];maxFileBytes:number;storageBudgetBytes:number}
export interface ContentAdapter {
 listComments(scenario:string,place:string,options?:{offset?:number}):Promise<{comments:PlaceComment[];nextOffset:number|null}>;
 createComment(scenario:string,place:string,text:string,options?:{parentId?:string|null}):Promise<CommentContribution>;
 updateComment(scenario:string,place:string,id:string,text:string,options:RevisionOptions):Promise<CommentContribution>;
 deleteComment(scenario:string,place:string,id:string,options:RevisionOptions):Promise<CommentContribution>;
 listCommentContributions(options?:ContributionFilter):Promise<{contributions:CommentContribution[];nextOffset:number|null}>;
 reviewComment(id:string,options:{action:'approve'|'reject';note:string}):Promise<CommentContribution>;

 listMedia(scenario:string):Promise<MediaLibrary>;
 uploadMedia(scenario:string,filename:string,bytes:BodyInit):Promise<MediaUpload>;
 deleteUnusedMedia(scenario:string,id:string):Promise<{ok:boolean}>;
 listPlaces(scenario:string):Promise<{scenario?:string;places:Place[]}>;
 getPlace(scenario:string,id:string):Promise<Place>;
 createPlace(scenario:string,place:PlaceInput,options?:{note?:string}):Promise<Contribution>;
 updatePlace(scenario:string,id:string,place:Partial<PlaceInput>,options:RevisionOptions):Promise<Contribution>;
 deletePlace(scenario:string,id:string,options:RevisionOptions):Promise<Contribution>;
 listContributions(options?:ContributionFilter):Promise<{contributions:Contribution[];nextOffset:number|null}>;
 reviewContribution(id:string,options:{action:'approve'|'reject';note:string}):Promise<Contribution>;
}
export type ScenarioId = string;
export interface Coordinates {lat:number;lon:number;height?:number}
export interface EmbedSession {id:string;url:string;expires:number;origin:string;scenario:ScenarioId;place:string|null}
export interface Place {[attribute:string]:unknown;contentVersion:number;id:string;name:string;lat:number;lon:number;scenario:string;url:string;embedOptions:{scenario:string;place:string};kind?:string}
export class RealverseError extends Error {status:number}
export interface RealverseClient extends ContentAdapter,MapplsAdapter {}
export class RealverseClient {
 capabilities():Promise<Record<string,unknown>>;
 importPlaces(scenario:string,places:PlaceInput[],options?:{note?:string}):Promise<{contributions:Contribution[]}>;
 constructor(options:{baseUrl:string;apiKey:string});
 listScenarios():Promise<{version:string;scenarios:Array<{id:string;name:string;category:string;lat:number;lon:number;bbox:number[]}>}>;
 getScenario(id:ScenarioId):Promise<Record<string,unknown>>;
 listPlaces(id:ScenarioId):Promise<{scenario:string;places:Place[]}>;
 getPlace(scenario:ScenarioId,id:string):Promise<Place>;
 listTours(id:ScenarioId):Promise<{scenario:string;tours:Array<{id:string;name:string;stops:string[]}>}>;
 createEmbedSession(options:{scenario:ScenarioId;place?:string;origin?:string;ttlSeconds?:number}):Promise<EmbedSession>;
}
export interface RealverseWidget extends ContentAdapter,MapplsAdapter {}
export class RealverseWidget extends EventTarget {
 openSearch(mode?:'search'|'nearby'|'route'):Promise<unknown>;
 selectLocation(coordinates:Coordinates):Promise<unknown>;
 showRoute(route:{geometry:{type:'LineString';coordinates:number[][]}|string}):Promise<unknown>;
 clearRoute():Promise<unknown>;
 openCommunity(placeId?:string):Promise<unknown>;
 refreshPlaces():Promise<unknown>;
 constructor(element:HTMLElement,options:{sessionUrl:string;title?:string;content?:Partial<ContentAdapter & MapplsAdapter>});
 readonly iframe:HTMLIFrameElement;readonly ready:Promise<RealverseWidget>;
 flyTo(coordinates:Coordinates):Promise<unknown>;
 visitPlace(id:string,options?:{avatar?:boolean}):Promise<unknown>;
 setCameraMode(mode:'free'|'first'|'follow'|'orbit'|'bird'|'front'|'shoulder'):Promise<unknown>;
 placeAvatars(coordinates:Coordinates):Promise<unknown>;
 getState():Promise<Record<string,unknown>>;
 dispose():void;
}

export interface PlaceComment {id:string;placeId:string;parentId:string|null;text:string;author:string;version:number;deleted:boolean;canEdit:boolean;approvalStatus:'approved'|'awaiting-approval';created:number;updated:number}
export interface CommentContribution {id:string;comment_id:string;scenario:string;status:'pending'|'published'|'rejected';base_version:number;applied_version:number|null;reviewer:string|null;payload:Record<string,unknown>;previous:Record<string,unknown>|null}
