#!/usr/bin/env bash

# there are more things to edit at the end of this file

# you may want to change these variables

DB_DIR=~/.ch2
TMP_DIR=/tmp

SRC='0-27'
DST='0-28'

# these allow you to skip parts of the logic if re-doing a migration (expert only)
DO_COPY=1
DO_DROP=$DO_COPY  # because of topic rewrite drop requires copy
DO_DUMP=1

# we need python
source env/bin/activate

# this section of the script copies diary data and kit data across

if ((DO_COPY)); then
  echo "ensuring write-ahead file for $DB_DIR/database-$SRC.sql is cleared"
  echo "(should print 'delete')"
  sqlite3 "$DB_DIR/database-$SRC.sql" 'pragma journal_mode=delete' || { echo 'database locked?'; exit 1; }
  echo "copying $DB_DIR/database-$SRC.sql to $TMP_DIR/copy-$SRC.sql"
  rm -f "$TMP_DIR/copy-$SRC.sql"
  cp "$DB_DIR/database-$SRC.sql" "$TMP_DIR/copy-$SRC.sql"
fi

if ((DO_DROP)); then
  echo "dropping activity data from $TMP_DIR/copy-$SRC.sql"
  sqlite3 "$TMP_DIR/copy-$SRC.sql" <<EOF
  pragma foreign_keys = on;
  -- don't delete topic and kit data, and keep composite for next step
  delete from source where type not in (3, 9, 10, 7, 11);
  -- clean composite data
  delete from source where id in (
    select id from (
      select composite_source.id, composite_source.n_components as target, count(composite_component.id) as actual
        from composite_source left outer join composite_component
          on composite_source.id = composite_component.output_source_id
       group by composite_component.id
    ) where target != actual
  );
  delete from source where id in (
    select id from composite_source where n_components = 0
  );
  delete from statistic_name where id in (
    select statistic_name.id from statistic_name
      left outer join statistic_journal
        on statistic_journal.statistic_name_id = statistic_name.id
     where statistic_journal.id is null
       and statistic_name.owner not in ('DiaryTopic', 'ActivityTopic')
  );
  -- remove statistic names used by constants
  delete from statistic_name where owner = 'Constant';
  -- remove puns from package names
  update topic_field set display_cls = replace(display_cls, 'uweird', 'urwid');
  -- rename topic to diary_topic
  -- it is CRITICAL that the declarations here match the field order generated by sqlalchemy
  -- as loading is by position, not name
  create table diary_topic (
         id integer not null,
         name text default '' not null,
         description text default '' not null,
         sort integer default '0' not null,
         parent_id integer,
         schedule text not null,
         start integer,
         finish integer,
         primary key (id),
         foreign key(parent_id) references diary_topic (id)
  );
  create table diary_topic_field (
         id integer not null,
         sort integer default '0' not null,
         model text default '{}' not null,
         diary_topic_id integer not null,
         schedule text default '' not null,
         statistic_name_id integer not null,
         primary key (id),
         foreign key(diary_topic_id) references diary_topic (id) on delete cascade,
         foreign key(statistic_name_id) references statistic_name (id) on delete cascade
  );
  create table diary_topic_journal (
         id integer not null,
         diary_topic_id integer,
         date integer not null,
         primary key (id),
         foreign key(id) references source (id) on delete cascade,
         foreign key(diary_topic_id) references diary_topic (id)
  );
  create index ix_diary_topic_journal_date on diary_topic_journal (date);
  pragma foreign_keys = off;
  insert into diary_topic (id, parent_id, schedule, start, finish, name, description, sort)
  select id, parent_id, schedule, start, finish, name, description, sort
    from topic;
  insert into diary_topic_field (id, diary_topic_id, sort, statistic_name_id, schedule, model)
  select id, topic_id, sort, statistic_name_id, schedule, display_kargs
    from topic_field;
  insert into diary_topic_journal select * from topic_journal;
  drop table topic;
  drop table topic_field;
  drop table topic_journal;
  update statistic_name set owner='DiaryTopic' where owner='Topic';
  update statistic_name set "constraint"='Diary' || "constraint" where "constraint" like 'Topic%';
  pragma foreign_keys = on;
  -- ancient typo
  update diary_topic set name='Injuries' where name='Inuries';
  update diary_topic set name='Status' where name='Diary';
  update statistic_name set "constraint"='DiaryTopic "Status" (d)' where "constraint"='DiaryTopic "Diary" (d)';
EOF
  python - <<EOF
from ch2.data import *
from ch2.sql.tables import *

s = session('-v5 --dev -f $TMP_DIR/copy-$SRC.sql')
for field in s.query(DiaryTopicField).all():
    model = dict(field.model)
    # this isn't wonderful, but the number of fields is small and the number of users smaller
    if field.statistic_name.name in ('Sleep', 'Weight'):
        model['type'] = 'float'
    elif field.statistic_name.name == 'Rest HR':
        model['type'] = 'integer'
    elif field.statistic_name.name == 'Mood':
        model['type'] = 'score'
    else:
        model['type'] = 'edit'
    if 'width' in model:
        del model['width']
    if 'height' in model:
        del model['height']
    field.model = model

s.commit()
EOF
  python - <<EOF
from ch2.data import *
from ch2.sql.tables import *
from sqlalchemy import func

s = session('-v5 --dev -f $TMP_DIR/copy-$SRC.sql')
for row in s.query(func.distinct(DiaryTopicJournal.date)).all():
    date, saved = row[0], None
    for journal in s.query(DiaryTopicJournal).filter(DiaryTopicJournal.date == date).all():
        if saved:
            for statistic in s.query(StatisticJournal).filter(StatisticJournal.source == journal).all():
                statistic.source = saved
            s.delete(journal)
        else:
            saved = journal

s.commit()
EOF
fi

if ((DO_DUMP)); then
  echo "extracting data from $TMP_DIR/copy-$SRC.sql to load into new database"
  rm -f "$TMP_DIR/dump-$SRC.sql"
  # .commands cannot be indented?!
  sqlite3 "$TMP_DIR/copy-$SRC.sql" <<EOF
.output $TMP_DIR/dump-$SRC.sql
.mode insert source
select * from source;
.mode insert composite_source
select * from composite_source;
.mode insert composite_component
select * from composite_component;
.mode insert statistic_journal
select * from statistic_journal;
.mode insert statistic_journal_float
select * from statistic_journal_float;
.mode insert statistic_journal_integer
select * from statistic_journal_integer;
.mode insert statistic_journal_text
select * from statistic_journal_text;
.mode insert statistic_journal_timestamp
select * from statistic_journal_timestamp;
.mode insert statistic_name
select * from statistic_name;
.mode insert diary_topic
select * from diary_topic;
.mode insert diary_topic_field
select * from diary_topic_field;
.mode insert diary_topic_journal
select id, date from diary_topic_journal;
.mode insert segment
select * from segment;
.mode insert kit_group
select * from kit_group;
.mode insert kit_item
select * from kit_item;
.mode insert kit_component
select * from kit_component;
.mode insert kit_model
select * from kit_model;
EOF
fi

echo "creating new, empty database at $DB_DIR/database-$DST.sql"
echo "(should print warning config message)"
rm -f "$DB_DIR/database-$DST.sql"
rm -f "$DB_DIR/database-$DST.sql-shm"
rm -f "$DB_DIR/database-$DST.sql-wal"
rm -f "$DB_DIR/system-$DST.sql"
rm -f "$DB_DIR/system-$DST.sql-shm"
rm -f "$DB_DIR/system-$DST.sql-wal"
dev/ch2 no-op

echo "defining new activity fields"
python - <<EOF
from ch2.data import *
from ch2.sql import *
from ch2.config.database import *
from ch2.diary.model import *
from ch2.lib.schedule import Schedule
from datetime import date

s = session('-v5 --dev -f $DB_DIR/database-$DST.sql')
c = Counter()
add_activity_topic_field(s, None, 'Route', c, StatisticJournalType.TEXT, model={TYPE: EDIT})
add_activity_topic_field(s, None, 'Notes', c, StatisticJournalType.TEXT, model={TYPE: EDIT})
field = s.query(DiaryTopicField).join(StatisticName).filter(StatisticName.name == 'Route').one_or_none()
if field and not field.schedule:
    field.schedule = Schedule('-' + str(date.today()))
s.commit()
EOF

echo "loading data into $DB_DIR/database-$DST.sql"
sqlite3 "$DB_DIR/database-$DST.sql" < "$TMP_DIR/dump-$SRC.sql"

echo "adding default config to $DB_DIR/database-$DST.sql"
dev/ch2 --dev config default --no-diary


# you almost certainly want to change the following details

echo "adding personal constants to $DB_DIR/database-$DST.sql"
dev/ch2 --dev constants set FTHR.Bike 154
dev/ch2 --dev constants set FTHR.Walk 154
dev/ch2 --dev constants set SRTM1.Dir /home/andrew/archive/srtm1
# the name of this constant depends on the kit name and so we must add it ourselves
dev/ch2 --dev constants add --single Power.cotic \
  --description 'Bike namedtuple values to calculate power for this kit' \
  --validate ch2.stats.calculate.power.Bike
dev/ch2 --dev constants set Power.cotic '{"cda": 0.42, "crr": 0.0055, "weight": 12}'


echo "next, run 'ch2 activities' or similar to load data"
