# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)

import ipaddress
import json
import logging
import requests
from odoo import models, fields, api, _
from odoo.exceptions import UserError
from odoo.http import request as http_request

_logger = logging.getLogger(__name__)


class FusionClockLocation(models.Model):
    _name = 'fusion.clock.location'
    _description = 'Clock-In Location'
    _order = 'sequence, name'
    _rec_name = 'name'

    name = fields.Char(string='Location Name', required=True)
    address = fields.Char(string='Address')
    latitude = fields.Float(string='Latitude', digits=(10, 7))
    longitude = fields.Float(string='Longitude', digits=(10, 7))
    radius = fields.Integer(
        string='Radius (meters)',
        default=100,
        help="Geofence radius in meters. Employees must be within this distance to clock in/out.",
    )
    sequence = fields.Integer(default=10)
    company_id = fields.Many2one(
        'res.company',
        string='Company',
        default=lambda self: self.env.company,
        required=True,
    )
    active = fields.Boolean(default=True)
    employee_ids = fields.Many2many(
        'hr.employee',
        'fusion_clock_location_employee_rel',
        'location_id',
        'employee_id',
        string='Allowed Employees',
    )
    all_employees = fields.Boolean(
        string='All Employees',
        default=True,
        help="If checked, all employees can clock in/out at this location.",
    )
    color = fields.Char(string='Map Color', default='#10B981')
    note = fields.Text(string='Notes')
    timezone = fields.Selection(
        '_tz_get',
        string='Timezone',
        default=lambda self: self.env.user.tz or 'UTC',
    )

    # IP whitelist fallback
    ip_whitelist = fields.Text(
        string='IP Whitelist',
        help="One IP address or CIDR per line. Used as fallback when GPS is unavailable.",
    )

    # Photo verification
    require_photo = fields.Boolean(
        string='Require Photo on Clock-In',
        default=False,
        help="If enabled, employees must take a selfie when clocking in at this location.",
    )

    # Computed
    attendance_count = fields.Integer(
        string='Total Attendances',
        compute='_compute_attendance_count',
    )
    map_url = fields.Char(
        string='Map Preview URL',
        compute='_compute_map_url',
    )

    @api.model
    def _tz_get(self):
        import pytz
        return [(tz, tz) for tz in sorted(pytz.all_timezones_set)]

    @api.depends('latitude', 'longitude', 'radius')
    def _compute_map_url(self):
        api_key = self.env['ir.config_parameter'].sudo().get_param('fusion_clock.google_maps_api_key', '')
        for rec in self:
            if rec.latitude and rec.longitude and api_key:
                rec.map_url = (
                    f"https://maps.googleapis.com/maps/api/staticmap?"
                    f"center={rec.latitude},{rec.longitude}&zoom=16&size=600x300&maptype=roadmap"
                    f"&markers=color:green%7C{rec.latitude},{rec.longitude}"
                    f"&key={api_key}"
                )
            else:
                rec.map_url = False

    def _compute_attendance_count(self):
        for rec in self:
            rec.attendance_count = self.env['hr.attendance'].search_count([
                ('x_fclk_location_id', '=', rec.id),
            ])

    def check_ip_whitelist(self, client_ip):
        """Check if a client IP matches this location's whitelist.
        Returns True if matched, False otherwise.
        """
        if not self.ip_whitelist or not client_ip:
            return False
        try:
            client = ipaddress.ip_address(client_ip)
            for line in self.ip_whitelist.strip().split('\n'):
                line = line.strip()
                if not line or line.startswith('#'):
                    continue
                try:
                    network = ipaddress.ip_network(line, strict=False)
                    if client in network:
                        return True
                except ValueError:
                    continue
        except ValueError:
            return False
        return False

    def action_detect_ip(self):
        """Detect the IP the Odoo server sees from your browser and add it."""
        self.ensure_one()
        ip = ''
        if http_request:
            ip = http_request.httprequest.environ.get(
                'HTTP_X_FORWARDED_FOR', ''
            ).split(',')[0].strip()
            if not ip:
                ip = http_request.httprequest.remote_addr or ''

        if not ip:
            raise UserError(_("Could not detect your IP from the request."))

        existing = (self.ip_whitelist or '').strip()
        existing_lines = [l.strip() for l in existing.split('\n') if l.strip()] if existing else []
        if ip in existing_lines:
            return {
                'type': 'ir.actions.client',
                'tag': 'display_notification',
                'params': {
                    'title': _('Already Whitelisted'),
                    'message': _('IP %s is already in the whitelist.') % ip,
                    'type': 'warning',
                    'sticky': False,
                },
            }

        existing_lines.append(ip)
        self.ip_whitelist = '\n'.join(existing_lines)

        extra = ''
        try:
            resp = requests.get(f'https://ipapi.co/{ip}/json/', timeout=5)
            if resp.ok:
                data = resp.json()
                city = data.get('city', '')
                org = data.get('org', '')
                if city or org:
                    extra = f" ({', '.join(filter(None, [city, org]))})"
        except Exception:
            pass

        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('IP Detected & Added'),
                'message': _('Added %s%s to the whitelist. This is the IP the server sees from your browser.') % (ip, extra),
                'type': 'success',
                'sticky': False,
            },
        }

    def action_geocode_address(self):
        """Geocode the address to get lat/lng using Google Geocoding API.
        Falls back to Nominatim (OpenStreetMap) if Google fails.
        """
        self.ensure_one()
        if not self.address:
            raise UserError(_("Please enter an address first."))

        api_key = self.env['ir.config_parameter'].sudo().get_param('fusion_clock.google_maps_api_key', '')
        if api_key:
            try:
                url = 'https://maps.googleapis.com/maps/api/geocode/json'
                params = {'address': self.address, 'key': api_key}
                response = requests.get(url, params=params, timeout=10)
                data = response.json()

                if data.get('status') == 'OK' and data.get('results'):
                    location = data['results'][0]['geometry']['location']
                    self.write({
                        'latitude': location['lat'],
                        'longitude': location['lng'],
                    })
                    formatted = data['results'][0].get('formatted_address', '')
                    if formatted:
                        self.address = formatted
                    return {
                        'type': 'ir.actions.client',
                        'tag': 'display_notification',
                        'params': {
                            'title': _('Geocoding Successful'),
                            'message': _('Location set to: %s, %s') % (location['lat'], location['lng']),
                            'type': 'success',
                            'sticky': False,
                        },
                    }
                elif data.get('status') == 'REQUEST_DENIED':
                    _logger.warning("Google Geocoding API denied. Falling back to Nominatim.")
                else:
                    _logger.warning("Google geocoding returned: %s. Trying Nominatim.", data.get('status'))
            except requests.exceptions.RequestException as e:
                _logger.warning("Google geocoding network error: %s. Trying Nominatim.", e)

        try:
            url = 'https://nominatim.openstreetmap.org/search'
            params = {
                'q': self.address,
                'format': 'json',
                'limit': 1,
            }
            headers = {'User-Agent': 'FusionClock/1.0 (Odoo Module)'}
            response = requests.get(url, params=params, headers=headers, timeout=10)
            data = response.json()

            if data and len(data) > 0:
                lat = float(data[0]['lat'])
                lng = float(data[0]['lon'])
                display_name = data[0].get('display_name', '')
                self.write({
                    'latitude': lat,
                    'longitude': lng,
                })
                if display_name:
                    self.address = display_name
                return {
                    'type': 'ir.actions.client',
                    'tag': 'display_notification',
                    'params': {
                        'title': _('Geocoding Successful (via OpenStreetMap)'),
                        'message': _('Location set to: %s, %s') % (lat, lng),
                        'type': 'success',
                        'sticky': False,
                    },
                }
            else:
                raise UserError(_("Could not geocode address. No results found."))
        except requests.exceptions.RequestException as e:
            raise UserError(_("Network error during geocoding: %s") % str(e))

    def action_view_attendances(self):
        """Open attendance records for this location."""
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'name': _('Attendances at %s') % self.name,
            'res_model': 'hr.attendance',
            'view_mode': 'list,form',
            'domain': [('x_fclk_location_id', '=', self.id)],
            'context': {'default_x_fclk_location_id': self.id},
        }
