-- 11-consolidate-km-rental.sql
-- Consolidate km/rental model: new included_km_per_day, rental_type,
-- rewrite calculate_price / create_lead / qualify_lead / notify_lead_qualified,
-- add sales_order_set_total RPC.
-- Idempotent.

-- =============================================================================
-- A. Vehicles table changes
-- =============================================================================

alter table public.vehicles add column if not exists included_km_per_day integer not null default 150;

update public.vehicles set included_km_per_day = coalesce(max_daily_km, 150) where included_km_per_day = 150;

update public.vehicles set included_km_per_day = 200 where brand = 'Ferrari' and model = '296 GTB';

alter table public.vehicles add column if not exists price_per_km_eur numeric(10,2) not null default 1.50;

alter table public.vehicles drop column if exists max_daily_km;
alter table public.vehicles drop column if exists max_km_weekend;

-- =============================================================================
-- B. Leads table changes
-- =============================================================================

alter table public.leads add column if not exists rental_type text not null default 'weekend' check (rental_type in ('weekend','individuell'));
update public.leads set rental_type = 'weekend' where rental_type is null;
create index if not exists leads_rental_type_idx on public.leads (rental_type);

-- =============================================================================
-- C. Sales orders table changes
-- =============================================================================

alter table public.sales_orders add column if not exists rental_type text not null default 'weekend' check (rental_type in ('weekend','individuell'));
update public.sales_orders set rental_type = 'weekend' where rental_type is null;
create index if not exists sales_orders_rental_type_idx on public.sales_orders (rental_type);

-- =============================================================================
-- D. Rewrite calculate_price() RPC
-- =============================================================================

drop function if exists public.calculate_price(uuid, date, date);

create or replace function public.calculate_price(
    p_vehicle_id uuid,
    p_date_from date,
    p_date_to date
)
returns jsonb
language plpgsql
stable
security definer
as $$
declare
    v_vehicle record;
    v_total_days integer;
    v_weekend_days integer;
    v_weekdays integer;
    v_daily_subtotal integer;
    v_weekend_subtotal integer;
    v_subtotal_eur integer;
    v_vat_eur integer;
    v_total_eur integer;
    v_deposit_eur integer;
    v_included_km_per_day integer;
    v_price_per_km numeric(10,2);
    v_total_included_km integer;
    v_extra_km integer;
    v_extra_km_eur numeric(10,2);
    v_cur date;
    v_dow integer;
begin
    if p_vehicle_id is null or p_date_from is null or p_date_to is null then
        raise exception 'vehicle_id, date_from and date_to are required';
    end if;
    if p_date_to <= p_date_from then
        raise exception 'date_to must be after date_from';
    end if;

    select daily_price_eur, weekend_price_eur, kaution_eur, included_km_per_day, price_per_km_eur
      into v_vehicle
      from public.vehicles
     where id = p_vehicle_id;

    if not found then
        raise exception 'Vehicle not found';
    end if;

    v_total_days := (p_date_to - p_date_from);
    v_weekend_days := 0;
    v_cur := p_date_from;
    while v_cur < p_date_to loop
        v_dow := extract(isodow from v_cur); -- 6=Sat, 7=Sun
        if v_dow in (6, 7) then
            v_weekend_days := v_weekend_days + 1;
        end if;
        v_cur := v_cur + 1;
    end loop;
    v_weekdays := v_total_days - v_weekend_days;

    v_daily_subtotal := v_weekdays * v_vehicle.daily_price_eur;
    v_weekend_subtotal := v_weekend_days * (case when v_vehicle.weekend_price_eur > 0 then v_vehicle.weekend_price_eur else v_vehicle.daily_price_eur end);
    v_subtotal_eur := v_daily_subtotal + v_weekend_subtotal;
    v_vat_eur := round(v_subtotal_eur * 0.20);
    v_total_eur := v_subtotal_eur + v_vat_eur;
    v_deposit_eur := coalesce(nullif(v_vehicle.kaution_eur, 0), 5000);

    v_included_km_per_day := coalesce(v_vehicle.included_km_per_day, 150);
    v_total_included_km := v_total_days * v_included_km_per_day;
    v_price_per_km := coalesce(v_vehicle.price_per_km_eur, 1.50);
    v_extra_km := greatest(0, 0); -- extra km is determined by caller (frontend) based on expected usage
    v_extra_km_eur := v_extra_km * v_price_per_km;

    return jsonb_build_object(
        'total_days', v_total_days,
        'weekday_count', v_weekdays,
        'weekend_day_count', v_weekend_days,
        'daily_subtotal', v_daily_subtotal,
        'weekend_subtotal', v_weekend_subtotal,
        'subtotal_eur', v_subtotal_eur,
        'vat_eur', v_vat_eur,
        'total_eur', v_total_eur,
        'deposit_eur', v_deposit_eur,
        'daily_price_eur', v_vehicle.daily_price_eur,
        'weekend_price_eur', (case when v_vehicle.weekend_price_eur > 0 then v_vehicle.weekend_price_eur else v_vehicle.daily_price_eur end),
        'included_km_per_day', v_included_km_per_day,
        'total_included_km', v_total_included_km,
        'price_per_km_eur', v_price_per_km,
        'extra_km', v_extra_km,
        'extra_km_eur', v_extra_km_eur
    );
end;
$$;

grant execute on function public.calculate_price(uuid, date, date) to anon, authenticated, service_role;

-- =============================================================================
-- E. Rewrite create_lead() RPC
-- =============================================================================

drop function if exists public.create_lead(
    text, text, text, uuid, text, date, date, text, text
);
drop function if exists public.create_lead(
    text, text, text, uuid, text, date, date, text, text,
    integer, integer, integer, integer, integer, integer, integer, integer, integer,
    text, text
);

create or replace function public.create_lead(
    p_name text,
    p_email text,
    p_phone text default '',
    p_vehicle_id uuid default null,
    p_vehicle_label text default '',
    p_date_from date default null,
    p_date_to date default null,
    p_message text default '',
    p_source text default 'website',
    p_ip_address text default '',
    p_ip_country text default ''
)
returns uuid
language plpgsql
security definer
as $$
declare
    v_lead_id uuid;
    v_vehicle record;
    v_total_days integer := 0;
    v_weekend_days integer := 0;
    v_weekdays integer := 0;
    v_daily_subtotal integer := 0;
    v_weekend_subtotal integer := 0;
    v_subtotal_eur integer := 0;
    v_vat_eur integer := 0;
    v_total_eur integer := 0;
    v_deposit_eur integer := 0;
    v_rental_type text := 'weekend';
    v_cur date;
    v_dow integer;
begin
    if p_vehicle_id is not null and p_date_from is not null and p_date_to is not null and p_date_to > p_date_from then
        select daily_price_eur, weekend_price_eur, kaution_eur
          into v_vehicle
          from public.vehicles
         where id = p_vehicle_id;

        if found then
            v_total_days := (p_date_to - p_date_from);

            -- Auto-detect rental type: 2 days or less = weekend, more = individuell
            v_rental_type := 'weekend';
            if v_total_days > 2 then
                v_rental_type := 'individuell';
            end if;

            -- For individuell, set all pricing to 0
            if v_rental_type = 'individuell' then
                v_daily_subtotal := 0;
                v_weekend_subtotal := 0;
                v_subtotal_eur := 0;
                v_vat_eur := 0;
                v_total_eur := 0;
                v_deposit_eur := 0;
            else
                v_cur := p_date_from;
                while v_cur < p_date_to loop
                    v_dow := extract(isodow from v_cur);
                    if v_dow in (6, 7) then
                        v_weekend_days := v_weekend_days + 1;
                    end if;
                    v_cur := v_cur + 1;
                end loop;
                v_weekdays := v_total_days - v_weekend_days;

                v_daily_subtotal := v_weekdays * v_vehicle.daily_price_eur;
                v_weekend_subtotal := v_weekend_days * (case when v_vehicle.weekend_price_eur > 0 then v_vehicle.weekend_price_eur else v_vehicle.daily_price_eur end);
                v_subtotal_eur := v_daily_subtotal + v_weekend_subtotal;
                v_vat_eur := round(v_subtotal_eur * 0.20);
                v_total_eur := v_subtotal_eur + v_vat_eur;
                v_deposit_eur := coalesce(nullif(v_vehicle.kaution_eur, 0), 5000);
            end if;
        end if;
    end if;

    insert into public.leads (
        name, email, phone, vehicle_id, vehicle_label, date_from, date_to,
        message, source,
        daily_subtotal, weekend_subtotal, subtotal_eur, vat_eur, total_eur, deposit_eur,
        total_days, weekday_count, weekend_day_count, ip_address, ip_country,
        rental_type
    ) values (
        p_name, p_email, p_phone, p_vehicle_id, p_vehicle_label, p_date_from, p_date_to,
        p_message, p_source,
        v_daily_subtotal, v_weekend_subtotal, v_subtotal_eur, v_vat_eur, v_total_eur, v_deposit_eur,
        v_total_days, v_weekdays, v_weekend_days, p_ip_address, p_ip_country,
        v_rental_type
    )
    returning id into v_lead_id;
    return v_lead_id;
end;
$$;

grant execute on function public.create_lead(
    text, text, text, uuid, text, date, date, text, text, text, text
) to anon, authenticated, service_role;

-- =============================================================================
-- F. Rewrite qualify_lead() RPC
-- =============================================================================

create or replace function public.qualify_lead(p_lead_id uuid, p_notes text default '')
returns public.customers
language plpgsql
security invoker
as $$
declare
    v_lead public.leads;
    v_customer public.customers;
    v_sales_order public.sales_orders;
    v_user uuid := auth.uid();
    v_order_num text;
    v_year integer;
    v_count integer;
begin
    select * into v_lead from public.leads where id = p_lead_id for update;
    if not found then
        raise exception 'lead % not found', p_lead_id;
    end if;

    if v_lead.status = 'qualified' then
        select * into v_customer from public.customers where lower(email) = lower(v_lead.email) limit 1;
        return v_customer;
    end if;

    update public.leads
       set status       = 'qualified',
           is_active    = false,
           qualified_at = now(),
           qualified_by = v_user,
           admin_notes  = coalesce(nullif(p_notes, ''), admin_notes)
     where id = v_lead.id;

    insert into public.customers (lead_id, name, email, phone, notes, created_by)
    values (v_lead.id, v_lead.name, v_lead.email, v_lead.phone, coalesce(p_notes,''), v_user)
    on conflict ((lower(email))) do update
       set name       = excluded.name,
           phone      = excluded.phone,
           notes      = case when excluded.notes <> '' then excluded.notes else public.customers.notes end,
           updated_at = now()
    returning * into v_customer;

    v_year := extract(year from now())::integer;
    select coalesce(count(*), 0) + 1 into v_count
      from public.sales_orders
     where extract(year from created_at)::integer = v_year;
    v_order_num := 'SO-' || v_year || '-' || lpad(v_count::text, 4, '0');

    insert into public.sales_orders (
        customer_id, lead_id, order_number, private_notes,
        daily_subtotal, weekend_subtotal, subtotal_eur, vat_eur, total_eur, deposit_eur,
        total_days, weekday_count, weekend_day_count,
        date_from, date_to, vehicle_label, rental_type
    ) values (
        v_customer.id, v_lead.id, v_order_num, coalesce(v_lead.admin_notes, ''),
        coalesce(v_lead.daily_subtotal, 0), coalesce(v_lead.weekend_subtotal, 0),
        coalesce(v_lead.subtotal_eur, 0), coalesce(v_lead.vat_eur, 0),
        coalesce(v_lead.total_eur, 0), coalesce(v_lead.deposit_eur, 0),
        coalesce(v_lead.total_days, 0), coalesce(v_lead.weekday_count, 0),
        coalesce(v_lead.weekend_day_count, 0),
        v_lead.date_from, v_lead.date_to, v_lead.vehicle_label, v_lead.rental_type
    ) returning * into v_sales_order;

    insert into public.customer_attachments (customer_id, lead_id, sales_order_id, bucket, file_path, file_name, mime_type, kind, created_at)
    select v_customer.id, la.lead_id, v_sales_order.id, la.bucket, la.file_path, la.file_name, la.mime_type, la.kind, la.created_at
      from public.lead_attachments la
     where la.lead_id = v_lead.id
       and not exists (
           select 1 from public.customer_attachments ca
            where ca.customer_id = v_customer.id
              and ca.file_path = la.file_path
       );

    insert into public.sales_order_attachments (sales_order_id, bucket, file_path, file_name, mime_type, kind, created_at)
    select v_sales_order.id, la.bucket, la.file_path, la.file_name, la.mime_type, la.kind, la.created_at
      from public.lead_attachments la
     where la.lead_id = v_lead.id;

    return v_customer;
end;
$$;

-- =============================================================================
-- G. Rewrite notify_lead_qualified() trigger function
-- =============================================================================

create or replace function public.notify_lead_qualified()
returns trigger
language plpgsql
security definer
as $$
begin
    -- Skip notification for 'individuell' rental type
    if NEW.rental_type = 'individuell' then
        return NEW;
    end if;

    perform pg_notify('lead_qualified', json_build_object(
        'sales_order_id', NEW.id,
        'customer_id', NEW.customer_id,
        'lead_id', NEW.lead_id,
        'order_number', NEW.order_number,
        'total_eur', NEW.total_eur,
        'deposit_eur', NEW.deposit_eur,
        'date_from', NEW.date_from,
        'date_to', NEW.date_to,
        'vehicle_label', NEW.vehicle_label,
        'rental_type', NEW.rental_type
    )::text);
    return NEW;
end;
$$;

-- =============================================================================
-- H. New RPC: sales_order_set_total
-- =============================================================================

create or replace function public.sales_order_set_total(p_so_id uuid, p_total_eur integer)
returns void
language plpgsql
security invoker
as $$
declare
    v_so public.sales_orders;
begin
    select * into v_so from public.sales_orders where id = p_so_id for update;
    if not found then
        raise exception 'sales order % not found', p_so_id;
    end if;
    if v_so.rental_type != 'individuell' then
        raise exception 'can only set total for individuell orders';
    end if;
    update public.sales_orders
       set total_eur = p_total_eur, updated_at = now()
     where id = p_so_id;
end;
$$;

grant execute on function public.sales_order_set_total(uuid, integer) to authenticated;

-- =============================================================================
-- I. Final schema reload
-- =============================================================================

notify pgrst, 'reload schema';
