create type schedule_frequency as enum ('DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY');
create type schedule_end_condition as enum ('NEVER', 'AFTER_COUNT', 'BY_DATE');

create table schedules(
  id bigserial primary key,
  group_id bigint not null references groups(id) on delete cascade,
  created_by bigint not null references users(id),
  entry_type entry_type not null default 'SPENDING',
  amount_dollars numeric(12,2) not null check (amount_dollars >= 0),
  necessity spending_necessity not null,
  purchase_type text not null,
  notes text,
  starts_on date not null,
  frequency schedule_frequency not null,
  interval_count integer not null default 1 check (interval_count > 0),
  end_condition schedule_end_condition not null default 'NEVER',
  end_count integer,
  end_date date,
  next_run_on date not null,
  last_run_on date,
  run_count integer not null default 0,
  is_active boolean not null default true,
  legacy_source_entry_id bigint references entries(id) on delete set null,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create index schedules_group_next_run_idx on schedules(group_id, next_run_on);
create index schedules_created_by_idx on schedules(created_by);

create table schedule_tags(
  schedule_id bigint not null references schedules(id) on delete cascade,
  tag_id bigint not null references tags(id) on delete cascade,
  created_at timestamptz not null default now(),
  primary key (schedule_id, tag_id)
);

create index schedule_tags_schedule_idx on schedule_tags(schedule_id);
create index schedule_tags_tag_idx on schedule_tags(tag_id);

alter table entries
  add column source_schedule_id bigint references schedules(id) on delete set null;

create unique index entries_schedule_occurrence_unique
  on entries(source_schedule_id, occurred_at)
  where source_schedule_id is not null;

with legacy_recurring as (
  select
    e.id,
    e.group_id,
    e.created_by,
    e.entry_type,
    e.amount_dollars,
    e.necessity,
    e.purchase_type,
    e.notes,
    e.occurred_at,
    case
      when e.frequency::text = 'BIWEEKLY' then 'WEEKLY'::schedule_frequency
      when e.frequency::text = 'QUARTERLY' then 'MONTHLY'::schedule_frequency
      when e.frequency::text in ('DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY') then e.frequency::text::schedule_frequency
      else 'MONTHLY'::schedule_frequency
    end as schedule_frequency,
    case
      when e.frequency::text = 'BIWEEKLY' then greatest(coalesce(e.interval_count, 1), 1) * 2
      when e.frequency::text = 'QUARTERLY' then greatest(coalesce(e.interval_count, 1), 1) * 3
      else greatest(coalesce(e.interval_count, 1), 1)
    end as safe_interval_count,
    coalesce(e.end_condition::text, 'NEVER')::schedule_end_condition as schedule_end_condition,
    e.end_count,
    e.end_date,
    coalesce(e.next_run_at, e.occurred_at) as legacy_base_date
  from entries e
  where e.is_recurring = true
), inserted_schedules as (
  insert into schedules(
    group_id,
    created_by,
    entry_type,
    amount_dollars,
    necessity,
    purchase_type,
    notes,
    starts_on,
    frequency,
    interval_count,
    end_condition,
    end_count,
    end_date,
    next_run_on,
    last_run_on,
    run_count,
    is_active,
    legacy_source_entry_id
  )
  select
    lr.group_id,
    lr.created_by,
    lr.entry_type,
    lr.amount_dollars,
    lr.necessity,
    lr.purchase_type,
    lr.notes,
    lr.legacy_base_date as starts_on,
    lr.schedule_frequency,
    lr.safe_interval_count,
    lr.schedule_end_condition,
    lr.end_count,
    lr.end_date,
    case
      when lr.schedule_frequency = 'DAILY' then (lr.legacy_base_date + (lr.safe_interval_count::text || ' day')::interval)::date
      when lr.schedule_frequency = 'WEEKLY' then (lr.legacy_base_date + (lr.safe_interval_count::text || ' week')::interval)::date
      when lr.schedule_frequency = 'MONTHLY' then (lr.legacy_base_date + (lr.safe_interval_count::text || ' month')::interval)::date
      else (lr.legacy_base_date + (lr.safe_interval_count::text || ' year')::interval)::date
    end as next_run_on,
    lr.legacy_base_date as last_run_on,
    1 as run_count,
    case
      when lr.schedule_end_condition = 'AFTER_COUNT' and coalesce(lr.end_count, 0) <= 1 then false
      when lr.schedule_end_condition = 'BY_DATE' and lr.end_date is not null and lr.end_date < lr.legacy_base_date then false
      else true
    end as is_active,
    lr.id as legacy_source_entry_id
  from legacy_recurring lr
  returning id, legacy_source_entry_id
)
insert into schedule_tags(schedule_id, tag_id)
select i.id, et.tag_id
from inserted_schedules i
join entry_tags et on et.entry_id = i.legacy_source_entry_id
on conflict do nothing;

update entries
set is_recurring = false,
    frequency = null,
    interval_count = 1,
    end_condition = null,
    end_count = null,
    end_date = null,
    next_run_at = null,
    last_executed_at = null
where is_recurring = true;
