from odoo import http, _
from odoo.http import request
import datetime
import pytz

class TableReservationController(http.Controller):

    @http.route(['/reservation'], type='http', auth="public", website=True)
    def reservation_form(self, **post):
        schedule = request.env['reservation.schedule'].sudo().search([])
        return request.render("dine360_reservation.reservation_page_template", {
            'schedule': schedule
        })

    def _get_slot_duration(self, schedule, time_float):
        """ Returns the slot duration for a given time based on peak hour settings """
        if not schedule:
            return 1.0
        for peak in schedule.peak_hour_ids:
            if peak.start_time <= time_float < peak.end_time:
                return float(peak.slot_duration)
        return float(schedule.default_slot_duration or 1.0)

    def _find_best_tables(self, floor, available_floor_tables, num_people):
        """ Find the best combination of nearby tables on a floor, preferring same zone """
        # Case 1: Single table fits
        singles = [t for t in available_floor_tables if t.min_party_size <= num_people <= t.max_party_size]
        if singles:
            best_single = min(singles, key=lambda t: t.max_party_size)
            return [best_single]
        
        # Case 2: Merge needed
        # Group tables by zone
        zones = {}
        for t in available_floor_tables:
            z = t.zone or 'Default'
            if z not in zones: zones[z] = []
            zones[z].append(t)
            
        best_overall_combo = []
        min_overall_dist = float('inf')

        # Try merging within each zone first
        for zone_name, zone_tables in zones.items():
            if sum(t.max_party_size for t in zone_tables) < num_people:
                continue
                
            for start_table in zone_tables:
                current_combo = [start_table]
                current_cap = start_table.max_party_size
                others = [t for t in zone_tables if t.id != start_table.id]
                
                while current_cap < num_people and others:
                    nearest = min(others, key=lambda o: min(
                        (((o.position_h or 0) - (c.position_h or 0))**2 + ((o.position_v or 0) - (c.position_v or 0))**2)**0.5 
                        for c in current_combo
                    ))
                    current_combo.append(nearest)
                    current_cap += nearest.max_party_size
                    others.remove(nearest)
                
                if current_cap >= num_people:
                    dist_sum = 0
                    for i in range(len(current_combo)):
                        for j in range(i + 1, len(current_combo)):
                            t1, t2 = current_combo[i], current_combo[j]
                            dist_sum += (((t1.position_h or 0) - (t2.position_h or 0))**2 + ((t1.position_v or 0) - (t2.position_v or 0))**2)**0.5
                    
                    if dist_sum < min_overall_dist:
                        min_overall_dist = dist_sum
                        best_overall_combo = current_combo

        # If no same-zone combo found, use the best combo across the whole floor
        if not best_overall_combo:
            for start_table in available_floor_tables:
                current_combo = [start_table]
                current_cap = start_table.max_party_size
                others = [t for t in available_floor_tables if t.id != start_table.id]
                while current_cap < num_people and others:
                    nearest = min(others, key=lambda o: min(
                        (((o.position_h or 0) - (c.position_h or 0))**2 + ((o.position_v or 0) - (c.position_v or 0))**2)**0.5 
                        for c in current_combo
                    ))
                    current_combo.append(nearest)
                    current_cap += nearest.max_party_size
                    others.remove(nearest)
                if current_cap >= num_people:
                    dist_sum = 0
                    for i in range(len(current_combo)):
                        for j in range(i + 1, len(current_combo)):
                            t1, t2 = current_combo[i], current_combo[j]
                            dist_sum += (((t1.position_h or 0) - (t2.position_h or 0))**2 + ((t1.position_v or 0) - (t2.position_v or 0))**2)**0.5
                    if dist_sum < min_overall_dist:
                        min_overall_dist = dist_sum
                        best_overall_combo = current_combo
                    
        return best_overall_combo

    @http.route(['/reservation/get_slots'], type='json', auth="public", website=True)
    def get_available_slots(self, date_str, num_people):
        try:
            res_date = datetime.datetime.strptime(date_str, '%Y-%m-%d').date()
            num_people = int(num_people)
        except:
            return {'error': 'Invalid date or guest count'}

        # 1. Check for Holiday
        holiday = request.env['reservation.holiday'].sudo().search([('date', '=', res_date)], limit=1)
        if holiday and holiday.is_closed:
            return {'error': 'The restaurant is closed on this day for %s.' % holiday.name}

        # 2. Get Schedule
        day = str(res_date.weekday())
        schedule = request.env['reservation.schedule'].sudo().search([('day', '=', day)], limit=1)
        if not schedule or schedule.is_closed:
            return {'error': 'Reservations are not available on this day.'}

        # Determine operating hours
        opening = holiday.opening_time if (holiday and holiday.opening_time) else schedule.opening_time
        closing = holiday.closing_time if (holiday and holiday.closing_time) else schedule.closing_time
        
        # 3. Generate Slots (intervals based on table duration)
        slots = []
        current_time = opening
        restaurant_tz = pytz.timezone('America/Toronto')
        now_local = datetime.datetime.now(restaurant_tz).replace(tzinfo=None)

        all_tables = request.env['restaurant.table'].sudo().search([('is_reservation_enabled', '=', True)])
        floors = request.env['restaurant.floor'].sudo().search([])

        while current_time < closing:
            duration = self._get_slot_duration(schedule, current_time)
            slot_start_dt = datetime.datetime.combine(res_date, datetime.time(int(current_time), int((current_time % 1) * 60)))
            slot_end_dt = slot_start_dt + datetime.timedelta(hours=duration)

            if slot_start_dt > now_local:
                # Check for Break
                is_in_break = False
                if schedule.has_break:
                    if current_time < schedule.break_end_time and (current_time + duration) > schedule.break_start_time:
                        is_in_break = True
                
                if not is_in_break:
                    best_available_combo = []
                    
                    for floor in floors:
                        floor_tables = all_tables.filtered(lambda t: t.floor_id == floor)
                        available_on_floor = []
                        for table in floor_tables:
                            overlap = request.env['restaurant.reservation'].sudo().search([
                                ('table_ids', 'in', [table.id]),
                                ('state', 'in', ['confirmed', 'draft']),
                                ('start_time', '<', slot_end_dt),
                                ('end_time', '>', slot_start_dt),
                            ])
                            if not overlap:
                                available_on_floor.append(table)
                        
                        combo = self._find_best_tables(floor, available_on_floor, num_people)
                        if combo and (not best_available_combo or len(combo) < len(best_available_combo)):
                            best_available_combo = combo
                            if len(combo) == 1: break # Perfect match found

                    if best_available_combo:
                        hours_int = int(current_time)
                        mins_int = int((current_time % 1) * 60)
                        ampm = "AM" if hours_int < 12 else "PM"
                        disp_h = hours_int if hours_int <= 12 else hours_int - 12
                        if disp_h == 0: disp_h = 12
                        
                        table_names = ", ".join([t.name for t in best_available_combo])
                        floor_name = best_available_combo[0].floor_id.name
                        
                        slots.append({
                            'time': f"{hours_int:02d}:{mins_int:02d}",
                            'display': f"{disp_h}:{mins_int:02d} {ampm}",
                            'full_dt': slot_start_dt.strftime('%Y-%m-%dT%H:%M'),
                            'tables': f"{floor_name} - {table_names}"
                        })
            
            # Use the calculated duration as the interval to respect user configuration
            current_time += duration

        return {'slots': slots}

    @http.route(['/reservation/submit'], type='http', auth="public", website=True, methods=['POST'], csrf=True)
    def reservation_submit(self, **post):
        # Extract data
        customer_name = post.get('customer_name')
        phone = post.get('phone')
        email = post.get('email')
        num_people = int(post.get('num_people', 1))
        start_time_str = post.get('start_time')
        
        if not start_time_str:
            return request.render("dine360_reservation.reservation_page_template", {'error': 'Please select a time slot.'})

        if not email:
            return request.render("dine360_reservation.reservation_page_template", {
                'error': 'Email is required.',
                'post': post,
            })

        # Convert start_time to datetime object and localize to restaurant timezone (America/Toronto)
        restaurant_tz = pytz.timezone('America/Toronto')
        try:
            local_start = datetime.datetime.strptime(start_time_str, '%Y-%m-%dT%H:%M')
            local_start = restaurant_tz.localize(local_start)
            # Convert to UTC for Odoo
            start_time = local_start.astimezone(pytz.utc).replace(tzinfo=None)
        except Exception:
            return request.render("dine360_reservation.reservation_page_template", {
                'error': _("Invalid date or time format."),
                'post': post,
            })
        
        res_date = local_start.date() # Use local_start for date to get correct weekday
        
        # Determine Duration
        day = str(res_date.weekday())
        schedule = request.env['reservation.schedule'].sudo().search([('day', '=', day)], limit=1)
        time_float = local_start.hour + local_start.minute / 60.0
        duration = self._get_slot_duration(schedule, time_float)
        end_time = start_time + datetime.timedelta(hours=duration)

        # FIND TABLES (Nearest Merge)
        assigned_tables = []
        target_floor = False
        
        all_floors = request.env['restaurant.floor'].sudo().search([])
        for floor in all_floors:
            floor_tables = request.env['restaurant.table'].sudo().search([
                ('floor_id', '=', floor.id),
                ('is_reservation_enabled', '=', True)
            ])
            
            available_tables = []
            for table in floor_tables:
                overlap = request.env['restaurant.reservation'].sudo().search([
                    ('table_ids', 'in', [table.id]),
                    ('state', 'in', ['confirmed', 'draft']),
                    ('start_time', '<', end_time),
                    ('end_time', '>', start_time),
                ])
                if not overlap:
                    available_tables.append(table)
            
            combo = self._find_best_tables(floor, available_tables, num_people)
            if combo and (not assigned_tables or len(combo) < len(assigned_tables)):
                assigned_tables = combo
                target_floor = floor
                if len(combo) == 1: break

        if not assigned_tables:
             return request.render("dine360_reservation.reservation_page_template", {'error': 'Sorry, no tables available for this time/group size.'})

        # Create Reservation
        try:
            reservation = request.env['restaurant.reservation'].sudo().create({
                'customer_name': customer_name,
                'phone': phone,
                'email': email,
                'num_people': num_people,
                'floor_id': target_floor.id,
                'table_ids': [(6, 0, [t.id for t in assigned_tables])],
                'table_id': assigned_tables[0].id,
                'start_time': start_time,
                'end_time': end_time,
                'state': 'confirmed' # Direct confirmation from website
            })

            # Send Emails
            try:
                import logging
                _logger = logging.getLogger(__name__)

                # Use the configured outgoing mail server's FROM address (must match SMTP username)
                outgoing_server = request.env['ir.mail_server'].sudo().search([], limit=1)
                smtp_from = outgoing_server.smtp_user if outgoing_server else 'alaguraj0361@gmail.com'
                
                # 1. Notify the Company (Admin)
                admin_mail_values = {
                    'subject': f"New Table Reservation: {customer_name}",
                    'body_html': f"""
                        <div style="font-family: Arial, sans-serif; padding: 20px;">
                            <h2 style="color: #2BB1A5;">New Table Reservation</h2>
                            <p>A new table reservation has been submitted from the website.</p>
                            <table style="width:100%; border-collapse: collapse;">
                                <tr><td style="padding:8px; font-weight:bold;">Customer Name:</td><td style="padding:8px;">{customer_name}</td></tr>
                                <tr><td style="padding:8px; font-weight:bold;">Phone:</td><td style="padding:8px;">{phone}</td></tr>
                                <tr><td style="padding:8px; font-weight:bold;">Email:</td><td style="padding:8px;">{email}</td></tr>
                                <tr><td style="padding:8px; font-weight:bold;">Number of Guests:</td><td style="padding:8px;">{num_people}</td></tr>
                                <tr><td style="padding:8px; font-weight:bold;">Reservation Time:</td><td style="padding:8px;">{start_time_str}</td></tr>
                            </table>
                        </div>
                    """,
                    'email_to': 'alaguraj0361@gmail.com',
                    'email_from': smtp_from,
                    'reply_to': email,
                }
                admin_mail = request.env['mail.mail'].sudo().create(admin_mail_values)
                admin_mail.send()
                _logger.info("Admin reservation notification sent to alaguraj0361@gmail.com")

                # 2. Notify the Customer
                customer_mail_values = {
                    'subject': "Reservation Confirmation - Shivasakthi",
                    'body_html': f"""
                        <div style="font-family: Arial, sans-serif; padding: 20px;">
                            <h2 style="color: #2BB1A5;">Reservation Confirmed!</h2>
                            <p>Dear {customer_name},</p>
                            <p>Your table reservation at <b>Shivasakthi Indian Kitchen Bar</b> has been confirmed successfully!</p>
                            <table style="width:100%; border-collapse: collapse; margin-top: 15px;">
                                <tr><td style="padding:8px; font-weight:bold;">Number of Guests:</td><td style="padding:8px;">{num_people}</td></tr>
                                <tr><td style="padding:8px; font-weight:bold;">Reservation Time:</td><td style="padding:8px;">{start_time_str}</td></tr>
                            </table>
                            <p style="margin-top:20px;">If you need to make any changes or have questions, please reply to this email or call us at <b>+1(647)856-2878</b>.</p>
                            <p>We look forward to seeing you!</p>
                            <p>Thank you,<br><b>Shivasakthi Indian Kitchen Bar</b></p>
                        </div>
                    """,
                    'email_to': email,
                    'email_from': smtp_from,
                    'reply_to': smtp_from,
                }
                customer_mail = request.env['mail.mail'].sudo().create(customer_mail_values)
                customer_mail.send()
                _logger.info("Customer reservation confirmation sent to %s", email)

            except Exception as e:
                import logging
                _logger = logging.getLogger(__name__)
                _logger.error("Failed to send reservation emails: %s", str(e))

            return request.render("dine360_reservation.reservation_success_template", {
                'reservation': reservation,
            })
        except Exception as e:
            return request.render("dine360_reservation.reservation_page_template", {
                'error': str(e),
                'post': post,
            })
