import secrets

from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError


class McsAppointmentBooking(models.Model):
    _name = "mcs.appointment.booking"
    _description = "Appointment Booking"
    _inherit = ["mail.thread", "mail.activity.mixin"]
    _order = "start desc"

    name = fields.Char(compute="_compute_name", store=True)
    appointment_type_id = fields.Many2one(
        "mcs.appointment.type", required=True, ondelete="restrict", tracking=True
    )
    company_id = fields.Many2one(
        related="appointment_type_id.company_id", store=True, readonly=True
    )
    user_id = fields.Many2one(
        related="appointment_type_id.user_id", string="Host", store=True, readonly=True
    )
    partner_id = fields.Many2one("res.partner", string="Customer")
    customer_name = fields.Char(required=True, tracking=True)
    customer_email = fields.Char(required=True, tracking=True)
    customer_phone = fields.Char()
    start = fields.Datetime(required=True, tracking=True)
    stop = fields.Datetime(required=True, tracking=True)
    duration = fields.Float(related="appointment_type_id.duration", readonly=True)
    state = fields.Selection(
        [
            ("draft", "Draft"),
            ("confirmed", "Confirmed"),
            ("cancelled", "Cancelled"),
        ],
        default="draft",
        required=True,
        tracking=True,
    )
    notes = fields.Text()
    calendar_event_id = fields.Many2one("calendar.event", readonly=True, copy=False)
    access_token = fields.Char(default=lambda self: secrets.token_urlsafe(24), copy=False)

    @api.depends("appointment_type_id", "customer_name", "start")
    def _compute_name(self):
        for booking in self:
            parts = [
                booking.appointment_type_id.name or "Appointment",
                booking.customer_name or "Customer",
            ]
            if booking.start:
                parts.append(fields.Datetime.to_string(booking.start))
            booking.name = " - ".join(parts)

    @api.constrains("start", "stop")
    def _check_dates(self):
        for booking in self:
            if booking.start and booking.stop and booking.start >= booking.stop:
                raise ValidationError("Appointment end must be after start.")

    def _ensure_partner(self):
        self.ensure_one()
        if self.partner_id:
            return self.partner_id
        partner = self.env["res.partner"].sudo().search(
            [("email", "=", self.customer_email)], limit=1
        )
        if not partner:
            partner = self.env["res.partner"].sudo().create(
                {
                    "name": self.customer_name,
                    "email": self.customer_email,
                    "phone": self.customer_phone,
                    "company_id": self.company_id.id,
                }
            )
        self.partner_id = partner.id
        return partner

    def action_confirm(self):
        for booking in self:
            if booking.state == "confirmed":
                continue
            busy = booking.appointment_type_id._busy_intervals(booking.start, booking.stop)
            busy = [
                interval
                for interval in busy
                if not booking.calendar_event_id
                or interval != (booking.calendar_event_id.start, booking.calendar_event_id.stop)
            ]
            if not booking.appointment_type_id._is_free(booking.start, booking.stop, busy):
                raise UserError("This time slot is no longer available.")

            partner = booking._ensure_partner()
            event_vals = {
                "name": booking.name,
                "start": booking.start,
                "stop": booking.stop,
                "user_id": booking.user_id.id,
                "partner_ids": [(6, 0, [partner.id, booking.user_id.partner_id.id])],
                "description": booking.notes or "",
            }
            if booking.calendar_event_id:
                booking.calendar_event_id.write(event_vals)
            else:
                booking.calendar_event_id = self.env["calendar.event"].sudo().create(event_vals).id
            booking.state = "confirmed"
        return True

    def action_cancel(self):
        for booking in self:
            booking.state = "cancelled"
            if booking.calendar_event_id:
                booking.calendar_event_id.sudo().unlink()
                booking.calendar_event_id = False
        return True
