# -*- coding: utf-8 -*-

from odoo import http, _, fields
from odoo.http import request
from odoo.addons.portal.controllers.portal import CustomerPortal
from odoo.exceptions import AccessError, ValidationError
from datetime import datetime, timedelta
import json
import logging
import pytz

_logger = logging.getLogger(__name__)


class PortalSchedule(CustomerPortal):
    """Portal controller for appointment scheduling and calendar management."""

    def _get_schedule_values(self):
        """Common values for schedule pages."""
        ICP = request.env['ir.config_parameter'].sudo()
        g_start = ICP.get_param('fusion_claims.portal_gradient_start', '#5ba848')
        g_mid = ICP.get_param('fusion_claims.portal_gradient_mid', '#3a8fb7')
        g_end = ICP.get_param('fusion_claims.portal_gradient_end', '#2e7aad')
        gradient = 'linear-gradient(135deg, %s 0%%, %s 60%%, %s 100%%)' % (g_start, g_mid, g_end)
        google_maps_api_key = ICP.get_param('fusion_claims.google_maps_api_key', '')

        return {
            'portal_gradient': gradient,
            'google_maps_api_key': google_maps_api_key,
        }

    def _get_user_timezone(self):
        tz_name = request.env.user.tz or 'America/Toronto'
        try:
            return pytz.timezone(tz_name)
        except pytz.exceptions.UnknownTimeZoneError:
            return pytz.timezone('America/Toronto')

    def _get_appointment_types(self):
        """Get appointment types available to the current user."""
        return request.env['appointment.type'].sudo().search([
            ('staff_user_ids', 'in', [request.env.user.id]),
        ])

    @http.route(['/my/schedule'], type='http', auth='user', website=True)
    def schedule_page(self, **kw):
        """Schedule overview: upcoming appointments and shareable link."""
        partner = request.env.user.partner_id
        user = request.env.user
        now = fields.Datetime.now()

        upcoming_events = request.env['calendar.event'].sudo().search([
            ('partner_ids', 'in', [partner.id]),
            ('start', '>=', now),
        ], order='start asc', limit=20)

        today_events = request.env['calendar.event'].sudo().search([
            ('partner_ids', 'in', [partner.id]),
            ('start', '>=', now.replace(hour=0, minute=0, second=0)),
            ('start', '<', (now + timedelta(days=1)).replace(hour=0, minute=0, second=0)),
        ], order='start asc')

        invite = request.env['appointment.invite'].sudo().search([
            ('staff_user_ids', 'in', [user.id]),
        ], limit=1)
        share_url = invite.book_url if invite else ''

        appointment_types = self._get_appointment_types()
        tz = self._get_user_timezone()

        values = self._get_schedule_values()
        values.update({
            'page_name': 'schedule',
            'upcoming_events': upcoming_events,
            'today_events': today_events,
            'share_url': share_url,
            'appointment_types': appointment_types,
            'user_tz': tz,
            'now': now,
        })
        return request.render('fusion_authorizer_portal.portal_schedule_page', values)

    @http.route(['/my/schedule/book'], type='http', auth='user', website=True)
    def schedule_book(self, appointment_type_id=None, **kw):
        """Booking form for a new appointment."""
        appointment_types = self._get_appointment_types()
        if not appointment_types:
            return request.redirect('/my/schedule')

        if appointment_type_id:
            selected_type = request.env['appointment.type'].sudo().browse(int(appointment_type_id))
            if not selected_type.exists():
                selected_type = appointment_types[0]
        else:
            selected_type = appointment_types[0]

        values = self._get_schedule_values()
        values.update({
            'page_name': 'schedule_book',
            'appointment_types': appointment_types,
            'selected_type': selected_type,
            'now': fields.Datetime.now(),
            'error': kw.get('error'),
            'success': kw.get('success'),
        })
        return request.render('fusion_authorizer_portal.portal_schedule_book', values)

    @http.route('/my/schedule/available-slots', type='json', auth='user', website=True)
    def schedule_available_slots(self, appointment_type_id, selected_date=None, **kw):
        """JSON-RPC endpoint: return available time slots for a date."""
        appointment_type = request.env['appointment.type'].sudo().browse(int(appointment_type_id))
        if not appointment_type.exists():
            return {'error': 'Appointment type not found', 'slots': []}

        user = request.env.user
        tz_name = user.tz or 'America/Toronto'
        tz = self._get_user_timezone()

        ref_date = fields.Datetime.now()
        slot_data = appointment_type._get_appointment_slots(
            timezone=tz_name,
            filter_users=request.env['res.users'].sudo().browse(user.id),
            asked_capacity=1,
            reference_date=ref_date,
        )

        filtered_slots = []
        target_date = None
        if selected_date:
            try:
                target_date = datetime.strptime(selected_date, '%Y-%m-%d').date()
            except ValueError:
                return {'error': 'Invalid date format', 'slots': []}

        for month_data in slot_data:
            for week in month_data.get('weeks', []):
                for day_info in week:
                    if not day_info:
                        continue
                    day = day_info.get('day')
                    if target_date and day != target_date:
                        continue
                    for slot in day_info.get('slots', []):
                        slot_dt_str = slot.get('datetime')
                        if not slot_dt_str:
                            continue
                        filtered_slots.append({
                            'datetime': slot_dt_str,
                            'start_hour': slot.get('start_hour', ''),
                            'end_hour': slot.get('end_hour', ''),
                            'duration': slot.get('slot_duration', str(appointment_type.appointment_duration)),
                            'staff_user_id': slot.get('staff_user_id', user.id),
                        })

        available_dates = []
        if not target_date:
            seen = set()
            for month_data in slot_data:
                for week in month_data.get('weeks', []):
                    for day_info in week:
                        if not day_info:
                            continue
                        day = day_info.get('day')
                        if day and day_info.get('slots') and str(day) not in seen:
                            seen.add(str(day))
                            available_dates.append(str(day))

        return {
            'slots': filtered_slots,
            'available_dates': sorted(available_dates),
            'duration': appointment_type.appointment_duration,
            'timezone': tz_name,
        }

    @http.route('/my/schedule/week-events', type='json', auth='user', website=True)
    def schedule_week_events(self, selected_date, **kw):
        """Return the user's calendar events for the Mon-Sun week containing selected_date."""
        try:
            target = datetime.strptime(selected_date, '%Y-%m-%d').date()
        except (ValueError, TypeError):
            return {'error': 'Invalid date format', 'events': [], 'week_days': []}

        monday = target - timedelta(days=target.weekday())
        sunday = monday + timedelta(days=6)

        partner = request.env.user.partner_id
        tz = self._get_user_timezone()

        monday_start_local = tz.localize(datetime.combine(monday, datetime.min.time()))
        sunday_end_local = tz.localize(datetime.combine(sunday, datetime.max.time()))
        monday_start_utc = monday_start_local.astimezone(pytz.utc).replace(tzinfo=None)
        sunday_end_utc = sunday_end_local.astimezone(pytz.utc).replace(tzinfo=None)

        events = request.env['calendar.event'].sudo().search([
            ('partner_ids', 'in', [partner.id]),
            ('start', '>=', monday_start_utc),
            ('start', '<=', sunday_end_utc),
        ], order='start asc')

        event_list = []
        for ev in events:
            start_utc = ev.start
            stop_utc = ev.stop
            start_local = pytz.utc.localize(start_utc).astimezone(tz)
            stop_local = pytz.utc.localize(stop_utc).astimezone(tz)
            event_list.append({
                'name': ev.name or '',
                'start': start_local.strftime('%Y-%m-%d %H:%M'),
                'end': stop_local.strftime('%Y-%m-%d %H:%M'),
                'start_time': start_local.strftime('%I:%M %p'),
                'end_time': stop_local.strftime('%I:%M %p'),
                'day_of_week': start_local.weekday(),
                'date': start_local.strftime('%Y-%m-%d'),
                'location': ev.location or '',
                'duration': ev.duration,
            })

        day_labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        week_days = []
        for i in range(7):
            day = monday + timedelta(days=i)
            week_days.append({
                'label': day_labels[i],
                'date': day.strftime('%Y-%m-%d'),
                'day_num': day.day,
                'is_selected': day == target,
            })

        return {
            'events': event_list,
            'week_days': week_days,
            'selected_date': selected_date,
        }

    @http.route('/my/schedule/book/submit', type='http', auth='user', website=True, methods=['POST'])
    def schedule_book_submit(self, **post):
        """Process the booking form submission."""
        appointment_type_id = int(post.get('appointment_type_id', 0))
        appointment_type = request.env['appointment.type'].sudo().browse(appointment_type_id)
        if not appointment_type.exists():
            return request.redirect('/my/schedule/book?error=Invalid+appointment+type')

        client_name = (post.get('client_name') or '').strip()
        client_street = (post.get('client_street') or '').strip()
        client_city = (post.get('client_city') or '').strip()
        client_province = (post.get('client_province') or '').strip()
        client_postal = (post.get('client_postal') or '').strip()
        notes = (post.get('notes') or '').strip()
        slot_datetime = (post.get('slot_datetime') or '').strip()
        slot_duration = post.get('slot_duration', str(appointment_type.appointment_duration))

        if not client_name or not slot_datetime:
            return request.redirect('/my/schedule/book?error=Client+name+and+time+slot+are+required')

        user = request.env.user
        tz = self._get_user_timezone()

        try:
            start_dt_naive = datetime.strptime(slot_datetime, '%Y-%m-%d %H:%M:%S')
            start_dt_local = tz.localize(start_dt_naive)
            start_dt_utc = start_dt_local.astimezone(pytz.utc).replace(tzinfo=None)
        except (ValueError, Exception) as e:
            _logger.error("Failed to parse slot datetime %s: %s", slot_datetime, e)
            return request.redirect('/my/schedule/book?error=Invalid+time+slot')

        duration = float(slot_duration)
        stop_dt_utc = start_dt_utc + timedelta(hours=duration)

        is_valid = appointment_type._check_appointment_is_valid_slot(
            staff_user=user,
            resources=request.env['appointment.resource'],
            asked_capacity=1,
            timezone=str(tz),
            start_dt=start_dt_utc,
            duration=duration,
            allday=False,
        )
        if not is_valid:
            return request.redirect('/my/schedule/book?error=This+slot+is+no+longer+available.+Please+choose+another+time.')

        address_parts = [p for p in [client_street, client_city, client_province, client_postal] if p]
        location = ', '.join(address_parts)

        description_lines = []
        if client_name:
            description_lines.append(f"Client: {client_name}")
        if location:
            description_lines.append(f"Address: {location}")
        if notes:
            description_lines.append(f"Notes: {notes}")
        description = '\n'.join(description_lines)

        event_name = f"{client_name} - {appointment_type.name}"

        booking_line_values = [{
            'appointment_user_id': user.id,
            'capacity_reserved': 1,
            'capacity_used': 1,
        }]

        try:
            event_vals = appointment_type._prepare_calendar_event_values(
                asked_capacity=1,
                booking_line_values=booking_line_values,
                description=description,
                duration=duration,
                allday=False,
                appointment_invite=request.env['appointment.invite'],
                guests=request.env['res.partner'],
                name=event_name,
                customer=user.partner_id,
                staff_user=user,
                start=start_dt_utc,
                stop=stop_dt_utc,
            )
            event_vals['location'] = location
            event = request.env['calendar.event'].sudo().create(event_vals)

            _logger.info(
                "Appointment booked: %s at %s (event ID: %s)",
                event_name, start_dt_utc, event.id,
            )
        except Exception as e:
            _logger.error("Failed to create appointment: %s", e)
            return request.redirect('/my/schedule/book?error=Failed+to+create+appointment.+Please+try+again.')

        return request.redirect('/my/schedule?success=Appointment+booked+successfully')
