set search_path = app, public;

create table if not exists app.end_of_day_reports (
  id                  uuid primary key default gen_random_uuid(),
  shop_id             uuid not null references app.shops(id) on delete restrict,
  business_date       date not null,
  submitted_at        timestamptz not null default now(),
  submitted_by        uuid not null references auth.users(id) on delete restrict,
  submitted_by_name   text not null,
  note                text,
  period_started_at   timestamptz not null,
  period_ended_at     timestamptz not null,
  completed_txn_count bigint not null default 0,
  voided_txn_count    bigint not null default 0,
  gross_usd           numeric(14,2) not null default 0,
  gross_lbp           numeric(18,0) not null default 0,
  safe_drop_usd       numeric(14,2) not null default 0,
  safe_drop_lbp       numeric(18,0) not null default 0,
  closed_shift_count  bigint not null default 0,
  total_variance_usd  numeric(14,2) not null default 0,
  total_variance_lbp  numeric(18,0) not null default 0,
  activity_count      bigint not null default 0,
  activity_log        jsonb not null default '[]'::jsonb,
  unique (shop_id, business_date)
);

alter table app.end_of_day_reports enable row level security;
alter table app.end_of_day_reports force  row level security;
revoke insert, update, delete on app.end_of_day_reports from authenticated;

drop policy if exists end_of_day_reports_select on app.end_of_day_reports;
create policy end_of_day_reports_select on app.end_of_day_reports
  for select to authenticated
  using (
    app.has_any_role_in_shop(shop_id,
      array['owner','auditor']::app.business_role[])
  );

grant select on app.end_of_day_reports to authenticated;

create or replace view app.v_end_of_day_reports as
select
  id,
  shop_id,
  business_date,
  submitted_at,
  submitted_by,
  submitted_by_name,
  note,
  period_started_at,
  period_ended_at,
  completed_txn_count,
  voided_txn_count,
  gross_usd,
  gross_lbp,
  safe_drop_usd,
  safe_drop_lbp,
  closed_shift_count,
  total_variance_usd,
  total_variance_lbp,
  activity_count,
  activity_log
from app.end_of_day_reports;

grant select on app.v_end_of_day_reports to authenticated;

create or replace function app.submit_end_of_day(
  p_shop uuid,
  p_note text default null
)
returns table (
  report_id uuid,
  business_date date,
  submitted_at timestamptz,
  completed_txn_count bigint,
  gross_usd numeric,
  gross_lbp numeric,
  activity_count bigint
)
language plpgsql
security definer
set search_path = app, public
as $$
declare
  v_period_started_at timestamptz;
  v_period_ended_at timestamptz := now();
  v_business_date date := (now() at time zone 'Asia/Beirut')::date;
  v_open_shift_count integer;
  v_report_id uuid;
  v_submitted_by_name text;
  v_completed_txn_count bigint;
  v_voided_txn_count bigint;
  v_gross_usd numeric(14,2);
  v_gross_lbp numeric(18,0);
  v_safe_drop_usd numeric(14,2);
  v_safe_drop_lbp numeric(18,0);
  v_closed_shift_count bigint;
  v_total_variance_usd numeric(14,2);
  v_total_variance_lbp numeric(18,0);
  v_activity_count bigint;
  v_activity_log jsonb;
begin
  if not app.has_role_in_shop(p_shop, 'owner') then
    raise exception 'owner role required';
  end if;

  select count(*)
    into v_open_shift_count
    from app.shifts
    where shop_id = p_shop
      and status <> 'closed';

  if v_open_shift_count > 0 then
    raise exception 'close or finalize all shifts before submitting end of day';
  end if;

  if exists (
    select 1
    from app.end_of_day_reports eod
    where eod.shop_id = p_shop
      and eod.business_date = v_business_date
  ) then
    raise exception 'end of day already submitted for this shop today';
  end if;

  select coalesce(
           max(period_ended_at),
           date_trunc('day', now() at time zone 'Asia/Beirut') at time zone 'Asia/Beirut'
         )
    into v_period_started_at
    from app.end_of_day_reports
    where shop_id = p_shop;

  select coalesce(full_name, email, auth.uid()::text)
    into v_submitted_by_name
    from auth.users
    where id = auth.uid();

  select
    count(*) filter (where t.status = 'completed'),
    count(*) filter (where t.status = 'voided'),
    coalesce(sum(t.gross_usd) filter (where t.status = 'completed'), 0),
    coalesce(sum(t.gross_lbp) filter (where t.status = 'completed'), 0)
    into v_completed_txn_count, v_voided_txn_count, v_gross_usd, v_gross_lbp
    from app.transactions t
    where t.shop_id = p_shop
      and t.occurred_at >= v_period_started_at
      and t.occurred_at <= v_period_ended_at;

  select
    coalesce(sum(case when cm.currency = 'USD' then abs(cm.amount) else 0 end), 0),
    coalesce(sum(case when cm.currency = 'LBP' then abs(cm.amount) else 0 end), 0)
    into v_safe_drop_usd, v_safe_drop_lbp
    from app.cash_movements cm
    join app.shifts sh on sh.id = cm.shift_id
    where sh.shop_id = p_shop
      and cm.type = 'drop_to_safe'
      and cm.occurred_at >= v_period_started_at
      and cm.occurred_at <= v_period_ended_at;

  select
    count(*),
    coalesce(sum(sh.variance_usd), 0),
    coalesce(sum(sh.variance_lbp), 0)
    into v_closed_shift_count, v_total_variance_usd, v_total_variance_lbp
    from app.shifts sh
    where sh.shop_id = p_shop
      and sh.status = 'closed'
      and sh.closed_at is not null
      and sh.closed_at >= v_period_started_at
      and sh.closed_at <= v_period_ended_at;

  select
    count(*),
    coalesce(
      jsonb_agg(
        jsonb_build_object(
          'occurred_at', ae.occurred_at,
          'event_type', ae.event_type,
          'metadata', ae.metadata
        )
        order by ae.occurred_at desc
      ),
      '[]'::jsonb
    )
    into v_activity_count, v_activity_log
    from app.auth_events ae
    where ae.shop_id = p_shop
      and ae.occurred_at >= v_period_started_at
      and ae.occurred_at <= v_period_ended_at
      and ae.event_type <> 'end_of_day_submitted';

  insert into app.end_of_day_reports(
    shop_id,
    business_date,
    submitted_by,
    submitted_by_name,
    note,
    period_started_at,
    period_ended_at,
    completed_txn_count,
    voided_txn_count,
    gross_usd,
    gross_lbp,
    safe_drop_usd,
    safe_drop_lbp,
    closed_shift_count,
    total_variance_usd,
    total_variance_lbp,
    activity_count,
    activity_log
  )
  values (
    p_shop,
    v_business_date,
    auth.uid(),
    coalesce(v_submitted_by_name, auth.uid()::text),
    nullif(btrim(coalesce(p_note, '')), ''),
    v_period_started_at,
    v_period_ended_at,
    coalesce(v_completed_txn_count, 0),
    coalesce(v_voided_txn_count, 0),
    coalesce(v_gross_usd, 0),
    coalesce(v_gross_lbp, 0),
    coalesce(v_safe_drop_usd, 0),
    coalesce(v_safe_drop_lbp, 0),
    coalesce(v_closed_shift_count, 0),
    coalesce(v_total_variance_usd, 0),
    coalesce(v_total_variance_lbp, 0),
    coalesce(v_activity_count, 0),
    coalesce(v_activity_log, '[]'::jsonb)
  )
  returning id into v_report_id;

  perform app.log_auth_event(
    'end_of_day_submitted',
    p_shop,
    null,
    jsonb_build_object(
      'report_id', v_report_id,
      'business_date', v_business_date,
      'completed_txn_count', coalesce(v_completed_txn_count, 0),
      'gross_usd', coalesce(v_gross_usd, 0),
      'gross_lbp', coalesce(v_gross_lbp, 0),
      'activity_count', coalesce(v_activity_count, 0)
    )
  );

  return query
  select
    r.id,
    r.business_date,
    r.submitted_at,
    r.completed_txn_count,
    r.gross_usd,
    r.gross_lbp,
    r.activity_count
  from app.end_of_day_reports r
  where r.id = v_report_id;
end;
$$;

revoke all on function app.submit_end_of_day(uuid, text) from public;
grant execute on function app.submit_end_of_day(uuid, text) to authenticated;

create or replace view app.v_owner_dashboard as
with latest_eod as (
  select distinct on (shop_id)
         shop_id,
         submitted_at
  from app.end_of_day_reports
  order by shop_id, submitted_at desc
),
periods as (
  select
    s.id as shop_id,
    coalesce(
      le.submitted_at,
      date_trunc('day', now() at time zone 'Asia/Beirut') at time zone 'Asia/Beirut'
    ) as period_started_at,
    le.submitted_at as last_end_of_day_at
  from app.shops s
  left join latest_eod le on le.shop_id = s.id
)
select
  s.id as shop_id,
  s.name as shop_name,
  (select count(*) from app.shifts where shop_id = s.id and status = 'open') as open_shifts,
  (select count(*) from app.alerts where shop_id = s.id and acknowledged_at is null) as open_alerts,
  (select count(*) from app.alerts where shop_id = s.id and acknowledged_at is null and severity = 'critical') as critical_alerts,
  (select count(*)
     from app.reconciliation_exceptions e
     join app.settlements st on st.id = e.settlement_id
     where st.shop_id = s.id
       and e.resolved_at is null) as open_recon_exceptions,
  (select coalesce(sum(t.gross_usd), 0)
     from app.transactions t
     where t.shop_id = s.id
       and t.status = 'completed'
       and t.occurred_at >= p.period_started_at) as today_gross_usd,
  (select coalesce(sum(t.gross_lbp), 0)
     from app.transactions t
     where t.shop_id = s.id
       and t.status = 'completed'
       and t.occurred_at >= p.period_started_at) as today_gross_lbp,
  p.period_started_at,
  p.last_end_of_day_at
from app.shops s
join periods p on p.shop_id = s.id;
