import logging
import os
from datetime import datetime as dt

from sqlmodel import Field, Session, SQLModel, create_engine, select, text, Relationship

logger = logging.getLogger(__name__)

DATE_FORMAT = '%Y-%m-%d'

def today():
    return dt.now().strftime(DATE_FORMAT)


class Vision(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str = Field(default=None, unique=True)
    complete_date: dt | None = Field(default=None)
    goals: list["Goal"] = Relationship(back_populates="vision")
    def __str__(self):
        return f'{self.name} (Vision {self.id})'


class PreferGoal(SQLModel, table=True):
    goal_id: int | None = Field(default=None, foreign_key="goal.id", primary_key=True)


class Goal(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str = Field(default=None, unique=True)
    complete_date: dt | None = Field(default=None)
    vision_id: int | None = Field(default=None, foreign_key="vision.id")
    notes: str | None = Field(default=None)
    tasks: list["Task"] = Relationship(back_populates="goal")
    vision: Vision = Relationship(back_populates="goals")
    def __str__(self):
        if not self.complete_date:
            return f'{self.name} (Goal {self.id})'
        else:
            completed = self.complete_date.strftime(DATE_FORMAT)
            return f'(Done {completed}) {self.name} (Goal {self.id})'

class Task(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str = Field(default=None, unique=True)
    goal_id: int | None = Field(default=None, foreign_key="goal.id")
    complete_date: dt | None = Field(default=None)
    goal: Goal = Relationship(back_populates="tasks")
    def __str__(self):
        return f'{self.name} ({self.id})'


def pretty_print(goal, tasks):
    results = []
    results.append(f' ## {goal}')
    if not tasks:
        results.append('-- Nothing to list --')

    for task in tasks:
        if(task.complete_date):
            results.append(f' - [x] {task}')
        else:
            results.append(f' - [ ] {task}')
    return results


class Data():
    def __init__(self, db_file=None):
        if not db_file:
            db_file = os.path.join(os.environ.get('MATRIX_DATA_DIR', ''), 'goal.db')

        self.file = db_file
        self.engine = self.get_engine()


    def get_engine(self):
        return create_engine(f"sqlite:///{self.file}")

    def setup(self):
        SQLModel.metadata.create_all(self.engine)
        return 'Success'


    def _set_pref_goal_id(self, goal_id):
        with Session(self.engine) as session:
            prefs = session.exec(select(PreferGoal)).all()
            for pref in prefs:
                session.delete(pref)
            pref = PreferGoal()
            pref.goal_id = goal_id
            session.add(pref)
            session.commit()

    def _get_pref_goal_id(self):
        with Session(self.engine) as session:
            pref = session.exec(select(PreferGoal)).one()
            return pref.goal_id



    def get_all_visions(self):
        with Session(self.engine) as session:
            cmd = select(Vision).where(Vision.complete_date == None)
            return session.exec(cmd).all()

    def get_all_goals(self):
        '''List all goals'''
        with Session(self.engine) as session:
            cmd = select(Goal).where(Goal.complete_date == None)
            return session.exec(cmd).all()


    def get_done_goals(self):
        '''List completed goals'''
        with Session(self.engine) as session:
            cmd = select(Goal).where(Goal.complete_date != None)
            return session.exec(cmd).all()


    def get_goal(self, goal_id):
        self._set_pref_goal_id(goal_id)
        with Session(self.engine) as session:
            cmd = select(Goal).where(Goal.id == goal_id)
            goal = session.exec(cmd).first()
            return (goal, goal.tasks)

    def get_vision(self, id):
        with Session(self.engine) as session:
            cmd = select(Vision).where(Vision.id == id)
            vision = session.exec(cmd).first()
            if vision:
                return (vision, vision.goals)
            else:
                return ('Not found', [])


    def complete_vision(self, id):
        with Session(self.engine) as session:
            cmd = select(Vision).where(Vision.id == id)
            vision = session.exec(cmd).first()
            vision.complete_date = dt.today()
            session.commit()


    def complete_goal(self, goal_id):
        self._set_pref_goal_id(goal_id)
        vision_id = -1
        with Session(self.engine) as session:
            cmd = select(Goal).where(Goal.id == goal_id)
            goal = session.exec(cmd).first()
            goal.complete_date = dt.today()
            vision_id = goal.vision_id
            session.commit()
        return vision_id

    def rename_goal(self, goal_id, name):
        self._set_pref_goal_id(goal_id)
        vision_id = -1
        with Session(self.engine) as session:
            cmd = select(Goal).where(Goal.id == goal_id)
            goal = session.exec(cmd).first()
            goal.name = name
            vision_id = goal.vision_id
            session.commit()
        return vision_id


    def complete_task(self, id):
        with Session(self.engine) as session:
            cmd = select(Task).where(Task.id == id)
            task = session.exec(cmd).first()
            task.complete_date = dt.today()
            goal_id = task.goal_id
            session.commit()
        self._set_pref_goal_id(goal_id)
        return goal_id

    def add_with_name(self, cls, name):
        id = -1
        with Session(self.engine) as session:
            new = cls(name=name)
            session.add(new)
            session.commit()
            id = new.id
        return id

    def add_vision(self, name):
        return self.add_with_name(Vision, name)
            
    def add_goal(self, name):
        goal_id = self.add_with_name(Goal, name)
        self._set_pref_goal_id(goal_id)
        return goal_id

    def quick_add_task(self, name):
        goal_id = self._get_pref_goal_id()
        task_id = self.add_with_name(Task, name)
        with Session(self.engine) as session:
            cmd = select(Task).where(Task.id == task_id)
            new = session.exec(cmd).first()
            new.goal_id = goal_id
            session.commit()
        return goal_id

    def remove_task(self, task_id):
        task_name = ''
        with Session(self.engine) as session:
            cmd = select(Task).where(Task.id  == task_id)
            to_remove = session.exec(cmd).first()
            task_name = to_remove.name
            session.delete(to_remove)
            session.commit()
        return f'Removed task "{task_name}"'

    def assign_vision(self, goal_id, vision_id):
        '''Goals may move between visions'''
        self._set_pref_goal_id(goal_id)
        with Session(self.engine) as session:
            cmd = select(Goal).where(Goal.id == goal_id)
            goal = session.exec(cmd).first()
            goal.vision_id = vision_id
            session.commit()

    def visionless(self):
        '''Get goals with no vision'''
        with Session(self.engine) as session:
            cmd = select(Goal).where(Goal.vision_id  == None)
            return session.exec(cmd).all()


    def add_note(self, note, goal):
        '''Notes print with the goal.'''
        pass

    # Someday: Notes on each

