import time
from datetime import datetime, timedelta, date

import typer
from sqlalchemy.exc import NoResultFound, OperationalError
from sqlmodel import Session, select, func
from tabulate import tabulate

from . import edit
from . import reports
from .database import create_db_and_tables, engine
from .functions_aux import Status, make_table_view, pop_up_msg
from .tables import ToDo, Timer

app = typer.Typer()
app.add_typer(reports.app, name='report', help='Print customized reports.')
app.add_typer(edit.app, name='edit', help='Edit records.')


@app.command()
def add(task: str, project: str = typer.Option(None, '--project', '-p'),
        due_date: datetime = typer.Option(None, '--due-date', '-d',
                                          formats=['%Y-%m-%d']),
        reminder: datetime = typer.Option(None, '--reminder', '-r',
                                          formats=['%Y-%m-%d']),
        status: Status = typer.Option(Status.to_do, '--status', '-s'),
        tag: str = typer.Option(None, '--tag', '-t')):
    """Add task to the to-do list."""
    try:
        today = datetime.today()

        if due_date is not None and due_date <= today:
            typer.secho(f'\ndue date must be grater than {today.date()}\n',
                        fg=typer.colors.RED)
            raise typer.Exit(code=1)

        if reminder is not None and reminder <= today:
            typer.secho(f'\nreminder must be grater than {today.date()}\n',
                        fg=typer.colors.RED)
            raise typer.Exit(code=1)

        if due_date is not None and reminder is not None and \
                reminder >= due_date:
            typer.secho(f'\nreminder must be smaller than {due_date.date()}\n',
                        fg=typer.colors.RED)
            raise typer.Exit(code=1)

        with Session(engine) as session:
            if project is not None:
                new_project = session.exec(select(ToDo).where(
                    ToDo.project == project)).first()
                if new_project is not None:
                    ongoing_project = session.exec(select(ToDo).where(
                        ToDo.project == project,
                        ToDo.status != 'done')).first()
                    if ongoing_project is None:
                        typer.secho(f'\nTasks already done in the project\n',
                                    fg=typer.colors.RED)
                        raise typer.Exit(code=1)

            new_entry = ToDo(task=task, project=project,
                             due_date=due_date, reminder=reminder,
                             status=status, tag=tag)
            session.add(new_entry)
            session.commit()

            new_id = session.exec(select(func.max(ToDo.id))).one()
            typer.secho(f'Add {task}. Task id: {new_id}\n',
                        fg=typer.colors.GREEN)
    except OperationalError:
        create_db_and_tables()
        add(task=task, project=project, due_date=due_date, reminder=reminder,
            status=status, tag=tag)


@app.command()
def start(task_id: int, duration: int = typer.Option(None, '--duration', '-d',
                                                     help='Duration in minutes')):
    """Start Timer for a given open task."""
    with Session(engine) as session:
        try:
            session.exec(select(Timer).where(Timer.end == None)).one()
            typer.secho('\nThe Timer must be stopped first\n',
                        fg=typer.colors.RED)
            raise typer.Exit(code=1)
        except NoResultFound:
            pass

        try:
            query = session.get(ToDo, task_id)
            if not query.status == 'done':
                if query.status == 'to do':
                    query.status = 'doing'
                    session.add(query)
                if duration is not None:
                    duration = timedelta(minutes=duration)
                    if duration <= timedelta(minutes=0):
                        typer.secho(
                            f'\nDuration must be grater than 0\n',
                            fg=typer.colors.RED)
                        raise typer.Exit(code=1)
                    total_seconds = int(duration.total_seconds())
                    session.add(Timer(id_todo=task_id))
                    session.commit()
                    new_id = session.exec(select(func.max(Timer.id))).one()
                    typer.secho(
                        f'\nTask Start task {task_id}. Timer id: {new_id}\n',
                        fg=typer.colors.GREEN)
                    with typer.progressbar(length=total_seconds) as progress:
                        end = datetime.utcnow() + duration
                        while datetime.utcnow() < end:
                            time.sleep(1)
                            progress.update(1)
                        else:
                            typer.secho('\n\nYour Time is over! Well done!\n',
                                        blink=True,
                                        fg=typer.colors.BRIGHT_GREEN)
                            pop_up_msg()
                            remark = typer.confirm("Any remark?")
                            if remark:
                                remark = typer.prompt('Enter your remarks.')
                            else:
                                remark = None
                            stop(remarks=remark)
                            typer.Exit()
                else:
                    session.add(Timer(id_todo=task_id))
                    session.commit()

                    new_id = session.exec(select(func.max(Timer.id))).one()
                    typer.secho(
                        f'\nStart task {task_id}. Timer id: {new_id}\n',
                        fg=typer.colors.GREEN)

            else:
                typer.secho(f'\nTask already done\n',
                            fg=typer.colors.RED)
                raise typer.Exit(code=1)

        except AttributeError:
            typer.secho(f'\nInvalid task id\n',
                        fg=typer.colors.RED)
            raise typer.Exit(code=1)


@app.command()
def stop(remarks: str = typer.Option(None, '--remarks', '-r')):
    """Stop Timer."""
    with Session(engine) as session:
        try:
            query_timer = session.exec(
                select(Timer).where(Timer.end == None)).one()
            query_timer.end = datetime.utcnow()
            query_timer.duration = query_timer.end - query_timer.start
            session.add(query_timer)

            query = session.get(ToDo, query_timer.id_todo)

            check = typer.confirm('Is the task done?')

            if not check and not remarks:
                pass
            else:
                if check:
                    query.status = 'done'
                    query.date_end = query_timer.end.date()
                if remarks:
                    query.remarks = remarks

            session.add(query)
            session.commit()

            new_id = session.exec(select(func.max(Timer.id))).one()
            typer.secho(
                f'\nStop task ({query.id}). Timer id: {new_id}\n',
                fg=typer.colors.GREEN)

        except NoResultFound:
            typer.secho(f'\nNo task running\n', fg=typer.colors.RED)
            raise typer.Exit(code=1)


@app.command()
def view(due_date: datetime = typer.Option(datetime.today() +
                                           timedelta(weeks=1),
                                           formats=['%Y-%m-%d'])):
    """Print to-do list view."""
    overdue = select(ToDo).where(ToDo.due_date < date.today(),
                                 ToDo.status != 'done').order_by(ToDo.due_date)

    reminders = select(ToDo).where(ToDo.reminder <= date.today(),
                                   ToDo.status != 'done').order_by(
        ToDo.due_date)

    due_in = select(ToDo).where(
        ToDo.due_date < due_date, ToDo.due_date >= date.today(),
        ToDo.status != 'done').order_by(ToDo.due_date)

    no_due = select(ToDo).where(
        ToDo.due_date == None, ToDo.status != 'done',
        ToDo.reminder == None).order_by(ToDo.date_init)

    if len(make_table_view(engine, overdue)) > 1:
        typer.secho(f'\nOVERDUE\n', fg=typer.colors.BRIGHT_RED,
                    bold=True)
        typer.secho(tabulate(make_table_view(engine, overdue),
                             headers="firstrow"), fg=typer.colors.BRIGHT_WHITE)

    if len(make_table_view(engine, reminders)) > 1:
        typer.secho(f'\nREMINDERS\n', fg=typer.colors.BRIGHT_YELLOW, bold=True)
        typer.secho(tabulate(make_table_view(engine, reminders),
                             headers="firstrow"), fg=typer.colors.BRIGHT_WHITE)

    if len(make_table_view(engine, due_in)) > 1:
        typer.secho(f'\nDUE IN {due_date.date()}\n',
                    fg=typer.colors.BRIGHT_GREEN, bold=True)
        typer.secho(tabulate(make_table_view(engine, due_in),
                             headers="firstrow"), fg=typer.colors.BRIGHT_WHITE)

    if len(make_table_view(engine, no_due)) > 1:
        typer.secho(f'\nNO DUE\n', fg=typer.colors.BRIGHT_BLUE, bold=True)
        typer.secho(tabulate(make_table_view(engine, no_due),
                             headers="firstrow"), fg=typer.colors.BRIGHT_WHITE)
    print('\n')

typer.secho typer.confirm datetime.datetime.utcnow typer.Option typer.Typer datetime.date.today datetime.timedelta time.sleep typer.prompt typer.Exit sqlmodel.select datetime.datetime.today sqlmodel.Session typer.progressbar sqlmodel.func.max
[((400, 413), 'typer.Typer', 'typer.Typer', ([], {}), '()\n', (411, 413), False, 'import typer\n'), ((600, 637), 'typer.Option', 'typer.Option', (['None', '"""--project"""', '"""-p"""'], {}), "(None, '--project', '-p')\n", (612, 637), False, 'import typer\n'), ((668, 728), 'typer.Option', 'typer.Option', (['None', '"""--due-date"""', '"""-d"""'], {'formats': "['%Y-%m-%d']"}), "(None, '--due-date', '-d', formats=['%Y-%m-%d'])\n", (680, 728), False, 'import typer\n'), ((801, 861), 'typer.Option', 'typer.Option', (['None', '"""--reminder"""', '"""-r"""'], {'formats': "['%Y-%m-%d']"}), "(None, '--reminder', '-r', formats=['%Y-%m-%d'])\n", (813, 861), False, 'import typer\n'), ((930, 974), 'typer.Option', 'typer.Option', (['Status.to_do', '"""--status"""', '"""-s"""'], {}), "(Status.to_do, '--status', '-s')\n", (942, 974), False, 'import typer\n'), ((995, 1028), 'typer.Option', 'typer.Option', (['None', '"""--tag"""', '"""-t"""'], {}), "(None, '--tag', '-t')\n", (1007, 1028), False, 'import typer\n'), ((3098, 3164), 'typer.Option', 'typer.Option', (['None', '"""--duration"""', '"""-d"""'], {'help': '"""Duration in minutes"""'}), "(None, '--duration', '-d', help='Duration in minutes')\n", (3110, 3164), False, 'import typer\n'), ((6176, 6213), 'typer.Option', 'typer.Option', (['None', '"""--remarks"""', '"""-r"""'], {}), "(None, '--remarks', '-r')\n", (6188, 6213), False, 'import typer\n'), ((1094, 1110), 'datetime.datetime.today', 'datetime.today', ([], {}), '()\n', (1108, 1110), False, 'from datetime import datetime, timedelta, date\n'), ((3274, 3289), 'sqlmodel.Session', 'Session', (['engine'], {}), '(engine)\n', (3281, 3289), False, 'from sqlmodel import Session, select, func\n'), ((6247, 6262), 'sqlmodel.Session', 'Session', (['engine'], {}), '(engine)\n', (6254, 6262), False, 'from sqlmodel import Session, select, func\n'), ((8242, 8308), 'typer.secho', 'typer.secho', (['f"""\nOVERDUE\n"""'], {'fg': 'typer.colors.BRIGHT_RED', 'bold': '(True)'}), "(f'\\nOVERDUE\\n', fg=typer.colors.BRIGHT_RED, bold=True)\n", (8253, 8308), False, 'import typer\n'), ((8533, 8604), 'typer.secho', 'typer.secho', (['f"""\nREMINDERS\n"""'], {'fg': 'typer.colors.BRIGHT_YELLOW', 'bold': '(True)'}), "(f'\\nREMINDERS\\n', fg=typer.colors.BRIGHT_YELLOW, bold=True)\n", (8544, 8604), False, 'import typer\n'), ((9114, 9180), 'typer.secho', 'typer.secho', (['f"""\nNO DUE\n"""'], {'fg': 'typer.colors.BRIGHT_BLUE', 'bold': '(True)'}), "(f'\\nNO DUE\\n', fg=typer.colors.BRIGHT_BLUE, bold=True)\n", (9125, 9180), False, 'import typer\n'), ((1306, 1324), 'typer.Exit', 'typer.Exit', ([], {'code': '(1)'}), '(code=1)\n', (1316, 1324), False, 'import typer\n'), ((1520, 1538), 'typer.Exit', 'typer.Exit', ([], {'code': '(1)'}), '(code=1)\n', (1530, 1538), False, 'import typer\n'), ((1784, 1802), 'typer.Exit', 'typer.Exit', ([], {'code': '(1)'}), '(code=1)\n', (1794, 1802), False, 'import typer\n'), ((1817, 1832), 'sqlmodel.Session', 'Session', (['engine'], {}), '(engine)\n', (1824, 1832), False, 'from sqlmodel import Session, select, func\n'), ((2772, 2842), 'typer.secho', 'typer.secho', (['f"""Add {task}. Task id: {new_id}\n"""'], {'fg': 'typer.colors.GREEN'}), "(f'Add {task}. Task id: {new_id}\\n', fg=typer.colors.GREEN)\n", (2783, 2842), False, 'import typer\n'), ((3398, 3471), 'typer.secho', 'typer.secho', (['"""\nThe Timer must be stopped first\n"""'], {'fg': 'typer.colors.RED'}), '("""\nThe Timer must be stopped first\n""", fg=typer.colors.RED)\n', (3409, 3471), False, 'import typer\n'), ((3512, 3530), 'typer.Exit', 'typer.Exit', ([], {'code': '(1)'}), '(code=1)\n', (3522, 3530), False, 'import typer\n'), ((6420, 6437), 'datetime.datetime.utcnow', 'datetime.utcnow', ([], {}), '()\n', (6435, 6437), False, 'from datetime import datetime, timedelta, date\n'), ((6627, 6661), 'typer.confirm', 'typer.confirm', (['"""Is the task done?"""'], {}), "('Is the task done?')\n", (6640, 6661), False, 'import typer\n'), ((7086, 7178), 'typer.secho', 'typer.secho', (['f"""\nStop task ({query.id}). Timer id: {new_id}\n"""'], {'fg': 'typer.colors.GREEN'}), '(f"""\nStop task ({query.id}). Timer id: {new_id}\n""", fg=typer.\n    colors.GREEN)\n', (7097, 7178), False, 'import typer\n'), ((7402, 7418), 'datetime.datetime.today', 'datetime.today', ([], {}), '()\n', (7416, 7418), False, 'from datetime import datetime, timedelta, date\n'), ((7464, 7482), 'datetime.timedelta', 'timedelta', ([], {'weeks': '(1)'}), '(weeks=1)\n', (7473, 7482), False, 'from datetime import datetime, timedelta, date\n'), ((5845, 5905), 'typer.secho', 'typer.secho', (['f"""\nTask already done\n"""'], {'fg': 'typer.colors.RED'}), '(f"""\nTask already done\n""", fg=typer.colors.RED)\n', (5856, 5905), False, 'import typer\n'), ((5954, 5972), 'typer.Exit', 'typer.Exit', ([], {'code': '(1)'}), '(code=1)\n', (5964, 5972), False, 'import typer\n'), ((6017, 6075), 'typer.secho', 'typer.secho', (['f"""\nInvalid task id\n"""'], {'fg': 'typer.colors.RED'}), '(f"""\nInvalid task id\n""", fg=typer.colors.RED)\n', (6028, 6075), False, 'import typer\n'), ((6116, 6134), 'typer.Exit', 'typer.Exit', ([], {'code': '(1)'}), '(code=1)\n', (6126, 6134), False, 'import typer\n'), ((7248, 7306), 'typer.secho', 'typer.secho', (['f"""\nNo task running\n"""'], {'fg': 'typer.colors.RED'}), '(f"""\nNo task running\n""", fg=typer.colors.RED)\n', (7259, 7306), False, 'import typer\n'), ((7323, 7341), 'typer.Exit', 'typer.Exit', ([], {'code': '(1)'}), '(code=1)\n', (7333, 7341), False, 'import typer\n'), ((3880, 3907), 'datetime.timedelta', 'timedelta', ([], {'minutes': 'duration'}), '(minutes=duration)\n', (3889, 3907), False, 'from datetime import datetime, timedelta, date\n'), ((4422, 4517), 'typer.secho', 'typer.secho', (['f"""\nTask Start task {task_id}. Timer id: {new_id}\n"""'], {'fg': 'typer.colors.GREEN'}), '(f"""\nTask Start task {task_id}. Timer id: {new_id}\n""", fg=\n    typer.colors.GREEN)\n', (4433, 4517), False, 'import typer\n'), ((5677, 5767), 'typer.secho', 'typer.secho', (['f"""\nStart task {task_id}. Timer id: {new_id}\n"""'], {'fg': 'typer.colors.GREEN'}), '(f"""\nStart task {task_id}. Timer id: {new_id}\n""", fg=typer.\n    colors.GREEN)\n', (5688, 5767), False, 'import typer\n'), ((7598, 7610), 'sqlmodel.select', 'select', (['ToDo'], {}), '(ToDo)\n', (7604, 7610), False, 'from sqlmodel import Session, select, func\n'), ((7633, 7645), 'datetime.date.today', 'date.today', ([], {}), '()\n', (7643, 7645), False, 'from datetime import datetime, timedelta, date\n'), ((7744, 7756), 'sqlmodel.select', 'select', (['ToDo'], {}), '(ToDo)\n', (7750, 7756), False, 'from sqlmodel import Session, select, func\n'), ((7780, 7792), 'datetime.date.today', 'date.today', ([], {}), '()\n', (7790, 7792), False, 'from datetime import datetime, timedelta, date\n'), ((7899, 7911), 'sqlmodel.select', 'select', (['ToDo'], {}), '(ToDo)\n', (7905, 7911), False, 'from sqlmodel import Session, select, func\n'), ((7970, 7982), 'datetime.date.today', 'date.today', ([], {}), '()\n', (7980, 7982), False, 'from datetime import datetime, timedelta, date\n'), ((8053, 8065), 'sqlmodel.select', 'select', (['ToDo'], {}), '(ToDo)\n', (8059, 8065), False, 'from sqlmodel import Session, select, func\n'), ((2290, 2366), 'typer.secho', 'typer.secho', (['f"""\nTasks already done in the project\n"""'], {'fg': 'typer.colors.RED'}), '(f"""\nTasks already done in the project\n""", fg=typer.colors.RED)\n', (2301, 2366), False, 'import typer\n'), ((2431, 2449), 'typer.Exit', 'typer.Exit', ([], {'code': '(1)'}), '(code=1)\n', (2441, 2449), False, 'import typer\n'), ((3943, 3963), 'datetime.timedelta', 'timedelta', ([], {'minutes': '(0)'}), '(minutes=0)\n', (3952, 3963), False, 'from datetime import datetime, timedelta, date\n'), ((3989, 4062), 'typer.secho', 'typer.secho', (['f"""\nDuration must be grater than 0\n"""'], {'fg': 'typer.colors.RED'}), '(f"""\nDuration must be grater than 0\n""", fg=typer.colors.RED)\n', (4000, 4062), False, 'import typer\n'), ((4148, 4166), 'typer.Exit', 'typer.Exit', ([], {'code': '(1)'}), '(code=1)\n', (4158, 4166), False, 'import typer\n'), ((4585, 4624), 'typer.progressbar', 'typer.progressbar', ([], {'length': 'total_seconds'}), '(length=total_seconds)\n', (4602, 4624), False, 'import typer\n'), ((2734, 2751), 'sqlmodel.func.max', 'func.max', (['ToDo.id'], {}), '(ToDo.id)\n', (2742, 2751), False, 'from sqlmodel import Session, select, func\n'), ((4668, 4685), 'datetime.datetime.utcnow', 'datetime.utcnow', ([], {}), '()\n', (4683, 4685), False, 'from datetime import datetime, timedelta, date\n'), ((4727, 4744), 'datetime.datetime.utcnow', 'datetime.utcnow', ([], {}), '()\n', (4742, 4744), False, 'from datetime import datetime, timedelta, date\n'), ((4780, 4793), 'time.sleep', 'time.sleep', (['(1)'], {}), '(1)\n', (4790, 4793), False, 'import time\n'), ((4899, 4997), 'typer.secho', 'typer.secho', (['"""\n\nYour Time is over! Well done!\n"""'], {'blink': '(True)', 'fg': 'typer.colors.BRIGHT_GREEN'}), '("""\n\nYour Time is over! Well done!\n""", blink=True, fg=typer.\n    colors.BRIGHT_GREEN)\n', (4910, 4997), False, 'import typer\n'), ((5150, 5178), 'typer.confirm', 'typer.confirm', (['"""Any remark?"""'], {}), "('Any remark?')\n", (5163, 5178), False, 'import typer\n'), ((5452, 5464), 'typer.Exit', 'typer.Exit', ([], {}), '()\n', (5462, 5464), False, 'import typer\n'), ((7047, 7065), 'sqlmodel.func.max', 'func.max', (['Timer.id'], {}), '(Timer.id)\n', (7055, 7065), False, 'from sqlmodel import Session, select, func\n'), ((3340, 3353), 'sqlmodel.select', 'select', (['Timer'], {}), '(Timer)\n', (3346, 3353), False, 'from sqlmodel import Session, select, func\n'), ((5259, 5294), 'typer.prompt', 'typer.prompt', (['"""Enter your remarks."""'], {}), "('Enter your remarks.')\n", (5271, 5294), False, 'import typer\n'), ((6344, 6357), 'sqlmodel.select', 'select', (['Timer'], {}), '(Timer)\n', (6350, 6357), False, 'from sqlmodel import Session, select, func\n'), ((1924, 1936), 'sqlmodel.select', 'select', (['ToDo'], {}), '(ToDo)\n', (1930, 1936), False, 'from sqlmodel import Session, select, func\n'), ((4375, 4393), 'sqlmodel.func.max', 'func.max', (['Timer.id'], {}), '(Timer.id)\n', (4383, 4393), False, 'from sqlmodel import Session, select, func\n'), ((5630, 5648), 'sqlmodel.func.max', 'func.max', (['Timer.id'], {}), '(Timer.id)\n', (5638, 5648), False, 'from sqlmodel import Session, select, func\n'), ((2093, 2105), 'sqlmodel.select', 'select', (['ToDo'], {}), '(ToDo)\n', (2099, 2105), False, 'from sqlmodel import Session, select, func\n')]