/** @odoo-module **/

import { Component, onMounted, onWillUnmount, useRef, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { standardFieldProps } from "@web/views/fields/standard_field_props";
import { rpc } from "@web/core/network/rpc";

export class FusionClockLocationMap extends Component {
    static template = "fusion_clock.LocationMap";
    static props = { ...standardFieldProps };

    setup() {
        this.mapRef = useRef("mapContainer");
        this.map = null;
        this.marker = null;
        this.circle = null;
        this._suppress = false;
        this._interval = null;
        this._AdvancedMarkerElement = null;

        this.state = useState({
            loading: true,
            error: "",
            mapVisible: false,
        });

        onMounted(() => this._init());
        onWillUnmount(() => this._cleanup());
    }

    get lat() { return this.props.record.data.latitude || 0; }
    get lng() { return this.props.record.data.longitude || 0; }
    get radius() { return this.props.record.data.radius || 100; }
    get color() { return this.props.record.data.color || "#10B981"; }
    get hasCoords() { return this.lat !== 0 || this.lng !== 0; }

    async _init() {
        const apiKey = await this._getApiKey();
        if (!apiKey) {
            this.state.loading = false;
            this.state.error = "Google Maps API key not configured. Set it in Fusion Clock Settings.";
            return;
        }
        try {
            await this._loadScript(apiKey);
        } catch {
            this.state.loading = false;
            this.state.error = "Failed to load Google Maps API.";
            return;
        }

        try {
            const { AdvancedMarkerElement } = await google.maps.importLibrary("marker");
            this._AdvancedMarkerElement = AdvancedMarkerElement;
        } catch {
            this.state.loading = false;
            this.state.error = "Failed to load marker library.";
            return;
        }

        this.state.loading = false;

        if (!this.hasCoords) {
            this._startWatcher();
            return;
        }

        this.state.mapVisible = true;
        await new Promise((r) => requestAnimationFrame(r));
        await new Promise((r) => requestAnimationFrame(r));
        this._buildMap();
    }

    _buildMap() {
        const el = this.mapRef.el;
        if (!el || !window.google || !this._AdvancedMarkerElement) return;

        const center = { lat: this.lat, lng: this.lng };

        this.map = new google.maps.Map(el, {
            center,
            zoom: 17,
            mapId: "DEMO_MAP_ID",
            mapTypeControl: true,
            mapTypeControlOptions: {
                style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
                position: google.maps.ControlPosition.TOP_RIGHT,
                mapTypeIds: ["roadmap", "satellite", "hybrid"],
            },
            streetViewControl: false,
            fullscreenControl: true,
            zoomControl: true,
            gestureHandling: "greedy",
        });

        this._placeMarker(center);
        this._drawCircle(center);

        if (!this.props.readonly) {
            this.map.addListener("click", (e) => {
                const pos = { lat: e.latLng.lat(), lng: e.latLng.lng() };
                this._placeMarker(pos);
                this._drawCircle(pos);
                this._suppress = true;
                this._saveCoords(pos.lat, pos.lng);
            });
        }

        this._startWatcher();
    }

    _placeMarker(pos) {
        if (this.marker) {
            this.marker.position = pos;
            return;
        }

        this.marker = new this._AdvancedMarkerElement({
            map: this.map,
            position: pos,
            gmpDraggable: !this.props.readonly,
            title: "Drag to fine-tune location",
        });

        if (!this.props.readonly) {
            this.marker.addListener("dragend", () => {
                const p = this.marker.position;
                const newPos = { lat: p.lat, lng: p.lng };
                this._drawCircle(newPos);
                this._suppress = true;
                this._saveCoords(newPos.lat, newPos.lng);
            });
        }
    }

    _drawCircle(center) {
        if (this.circle) {
            this.circle.setCenter(center);
            this.circle.setRadius(this.radius);
            this.circle.setOptions({ fillColor: this.color, strokeColor: this.color });
        } else {
            this.circle = new google.maps.Circle({
                map: this.map,
                center,
                radius: this.radius,
                fillColor: this.color,
                fillOpacity: 0.15,
                strokeColor: this.color,
                strokeOpacity: 0.6,
                strokeWeight: 2,
                clickable: false,
            });
        }
    }

    async _saveCoords(lat, lng) {
        if (this.props.readonly) return;
        await this.props.record.update({
            latitude: Math.round(lat * 10000000) / 10000000,
            longitude: Math.round(lng * 10000000) / 10000000,
        });
    }

    _startWatcher() {
        if (this._interval) return;
        this._lastLat = this.lat;
        this._lastLng = this.lng;
        this._lastRadius = this.radius;

        this._interval = setInterval(() => {
            const lat = this.lat;
            const lng = this.lng;
            const r = this.radius;

            const moved = Math.abs(this._lastLat - lat) > 0.0000001
                       || Math.abs(this._lastLng - lng) > 0.0000001;
            const resized = Math.abs(this._lastRadius - r) > 0.5;

            if (moved && this.map) {
                this._lastLat = lat;
                this._lastLng = lng;
                if (this._suppress) { this._suppress = false; return; }
                const pos = { lat, lng };
                this._placeMarker(pos);
                this._drawCircle(pos);
                this.map.panTo(pos);
            }

            if (resized && this.circle) {
                this._lastRadius = r;
                this.circle.setRadius(r);
            }

            if (!this.map && this.hasCoords && !this.state.error && this._AdvancedMarkerElement) {
                this.state.mapVisible = true;
                requestAnimationFrame(() => {
                    requestAnimationFrame(() => {
                        this._buildMap();
                    });
                });
            }
        }, 500);
    }

    async _getApiKey() {
        try {
            return await rpc("/web/dataset/call_kw", {
                model: "ir.config_parameter",
                method: "get_param",
                args: ["fusion_clock.google_maps_api_key", ""],
                kwargs: {},
            }) || "";
        } catch { return ""; }
    }

    async _loadScript(apiKey) {
        if (window.google && window.google.maps) return;
        return new Promise((resolve, reject) => {
            if (document.querySelector('script[src*="maps.googleapis.com"]')) {
                const t = setInterval(() => {
                    if (window.google && window.google.maps) { clearInterval(t); resolve(); }
                }, 100);
                setTimeout(() => { clearInterval(t); resolve(); }, 5000);
                return;
            }
            const s = document.createElement("script");
            s.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places&callback=__fclkMapCb`;
            s.async = true;
            s.defer = true;
            window.__fclkMapCb = () => { delete window.__fclkMapCb; resolve(); };
            s.onerror = () => reject(new Error("script load failed"));
            document.head.appendChild(s);
        });
    }

    _cleanup() {
        if (this._interval) clearInterval(this._interval);
        if (this.marker) { this.marker.map = null; this.marker = null; }
        if (this.circle) { this.circle.setMap(null); this.circle = null; }
        this.map = null;
    }
}

registry.category("fields").add("fclk_location_map", {
    component: FusionClockLocationMap,
    supportedTypes: ["char"],
});
