let table
let prevMonthBtn, nextMonthBtn, month

class Table {
    constructor(elmt) {
        this.table = elmt
        this.clockings = elmt.querySelector(".clockings")
        this.times = elmt.querySelector(".times")
        this.weekNames = elmt.querySelector(".week-names")
        this.dayNames = elmt.querySelector(".day-names")
        this.dayDates = elmt.querySelector(".day-dates")
        this.columns = elmt.querySelector(".columns")

        this.timeInputTemplate = getTemplate("time-input")

        this.nDays = 0

        let today = new Date()
        this.curMonth = today.getMonth()
        this.curYear = today.getFullYear()
        this.startDate = today
        this.endDate = today

        this.totalProjects = 0
        this.clockingTotals = []
        this.clockingRemotes = []

        this.update()
    }

    update() {
        let year = this.curYear.toString().padStart(4, "0")
        let month = (this.curMonth + 1).toString().padStart(2, "0")
        let date = new Date(`${year}-${month}-01`)
        let txt = MONTHS[this.curMonth]
        if (new Date().getFullYear() !== this.curYear) {
            txt += " " + year
        }
        document.getElementById("month").innerText = txt
        this.clear()
        this.addMonth(date)
        this.fetchData()
    }

    prevMonth() {
        this.curMonth--
        if (this.curMonth < 0) {
            this.curMonth += 12
            this.curYear -= 1
        }
        this.update()
    }

    nextMonth() {
        this.curMonth++
        if (this.curMonth >= 12) {
            this.curMonth -= 12
            this.curYear += 1
        }
        this.update()
    }

    clear() {
        this.columns.innerHTML = ""
        this.clockings.querySelectorAll(".dates th").forEach(c => c.remove())
        this.clockings.querySelectorAll(".clocking td").forEach(c => c.remove())
        this.weekNames.querySelectorAll("td").forEach(c => c.remove())
        this.dayNames.querySelectorAll("td").forEach(c => c.remove())
        this.dayDates.innerHTML = ""
        this.times.querySelectorAll("tr.project").forEach(r => r.remove())
        this.nDays = 0
    }

    addClockings(date) {
        let dates = this.clockings.querySelector(".dates")
        let dateCell = document.createElement("th")
        let weekDay = DAYS_SHORT[(date.getDay() + 6) % 7]
        let monthDay = date.getDate().toString().padStart(2, "0")
        dateCell.innerText = `${weekDay} ${monthDay}`
        dates.appendChild(dateCell)

        let clockings = this.clockings.querySelectorAll(".clocking")

        clockings.forEach((clocking, i) => {
            if (!(clocking.dataset.type || clocking.classList.contains("total"))) {
                return;
            }
            let cell = clocking.insertCell(this.nDays + 1)
            if (clocking.classList.contains("total")) {
                return
            }

            let inputCell = this.timeInputTemplate.cloneNode(true)
            let input = inputCell.querySelector("input")
            input.addEventListener("input", () => {
                this.setClocking(date, clocking.dataset.type, input.value)
            })
            cell.replaceWith(inputCell)
        })
    }

    addWeek(startDate, length=7) {
        let weekNum = startDate.getWeek()
        let weekName = this.weekNames.insertCell(-1)
        weekName.innerText = `Week n° ${weekNum}`
        weekName.colSpan = length
        for (let i = 0; i < length; i++) {
            let date = new Date(startDate.valueOf() + i * DAY_MS)
            let dayPadded = date.getDate().toString().padStart(2, "0")
            let monthPadded = (date.getMonth() + 1).toString().padStart(2, "0")

            this.addClockings(date)
            let dayName = this.dayNames.insertCell(this.nDays + 2)
            let dayDate = this.dayDates.insertCell(this.nDays)
            let weekDay = (date.getDay() + 6) % 7
            dayName.innerText = DAYS_SHORT[weekDay]
            dayDate.innerText = `${dayPadded}/${monthPadded}`

            let col = document.createElement("col")
            col.classList.add(`day-${weekDay}`)
            this.columns.appendChild(col)
            this.nDays++
        }
        let weekRemoteTotalCell = this.clockings.querySelector(".clocking.remote-total").insertCell(-1)
        weekRemoteTotalCell.colSpan = length
        let weekTotalCell = this.clockings.querySelector(".clocking.total2").insertCell(-1)
        weekTotalCell.colSpan = length
    }

    addProject(id, name, sagexNum, times, isParent=false, isProductive=false) {
        let row = this.times.insertRow(-1)
        row.classList.add("project")
        if (isParent) {
            row.classList.add("parent")
            if (isProductive) {
                row.classList.add("productive")
            }
        }
        row.dataset.id = id
        row.insertCell(-1).innerText = name
        row.insertCell(-1).innerText = sagexNum
        let total = 0
        times.forEach(time => {
            if (time) {
                total += time
            }
            let t = time.toString().split(".")
            if (t.length > 1) {
                t[1] = t[1].slice(0, 2)
            }
            row.insertCell(-1).innerText = t.join(".")
        })
        let totalStr = total.toString().split(".")
        if (totalStr.length > 1) {
            totalStr[1] = totalStr[1].slice(0, 2)
        }
        row.insertCell(-1).innerText = totalStr.join(".")

        if (isParent) {
            row.dataset.total = total
            if (isProductive) {
                this.totalProjects += total
            }
        }
        row.insertCell(-1)
        row.insertCell(-1)
        row.insertCell(-1)
    }

    addMonth(anchorDate) {
        this.startDate = new Date(anchorDate)
        this.startDate.setDate(1)

        let startDay = (this.startDate.getDay() + 6) % 7
        let length = 7 - startDay
        this.addWeek(this.startDate, length)
        let monday = new Date(this.startDate.valueOf() + length * DAY_MS)
        let nextMonday

        let startMonth = this.startDate.getMonth()

        while (monday.getMonth() === startMonth) {
            nextMonday = new Date(monday.valueOf() + 7 * DAY_MS)

            let len = 7
            if (nextMonday.getMonth() !== startMonth) {
                len -= nextMonday.getDate() - 1
            }
            this.addWeek(monday, len)
            monday = nextMonday
        }

        this.clockings.querySelector(".clocking.total").insertCell(-1).rowSpan = 2
        this.endDate = new Date(monday.valueOf() - monday.getDate() * DAY_MS)
    }

    fetchData() {
        let startDate = formatDate(this.startDate)
        let endDate = formatDate(this.endDate)
        fetch(`/table/${startDate}/${endDate}/`).then(res => {
            if (res.ok) {
                return res.json()
            }
        }).then(res => {
            if (res && res.status === "success") {
                this.displayData(res.data)
            }
        })
    }

    displayData(data) {
        this.displayClockings(data.clockings)
        this.totalProjects = 0
        data.parents.forEach(parent => {
            let parentDurations = Array(this.nDays).fill(0)

            let projects = parent.projects.map(project => {
                let durations = Array(this.nDays).fill("")
                let total = 0
                project.tasks.forEach(task => {
                    let date = new Date(task.date)
                    let i = Math.floor((date.valueOf() - this.startDate.valueOf()) / DAY_MS)
                    let hours = task.duration / 60
                    durations[i] = hours
                    total += hours
                    parentDurations[i] += hours
                })
                return {
                    id: project.id,
                    name: project.name,
                    durations: durations,
                    total: total
                }
            })

            this.addProject(
                parent.id,
                parent.name,
                parent.project_num,
                parentDurations.map(v => v === 0 ? "" : v),
                true,
                parent.is_productive
            )

            projects.filter(p => p.total !== 0).forEach(project => {
                this.addProject(project.id, project.name, "", project.durations)
            })
        })
        this.updateTotals()
    }

    displayClockings(clockings) {
        this.clockingTotals = Array(this.nDays).fill(0)
        this.clockingRemotes = Array(this.nDays).fill(0)
        clockings.forEach(clocking => {
            let date = new Date(clocking.date)
            if (date < this.startDate || date > this.endDate) {
                return
            }
            let i = Math.floor((date - this.startDate) / DAY_MS)
            this.clockings.querySelectorAll("tr.clocking[data-type]").forEach(row => {
                let type = row.dataset.type
                let cell = row.cells[i + 1]
                if (clocking[type]) {
                    cell.querySelector("input").value = clocking[type]
                }
            })
            let totalRow = this.clockings.querySelector(".clocking.total")
            let totalCell = totalRow.cells[i + 1]
            let hours = +clocking.total / 60
            this.clockingTotals[i] = hours
            totalCell.innerText = hours === 0 ? "" : Math.round(hours * 100) / 100

            if (clocking.remote !== null) {
                let remoteParts = clocking.remote.split(":").map(v => +v)
                let remoteHours = remoteParts[0] + remoteParts[1] / 60
                if (remoteParts.length >= 3) {
                    remoteHours += remoteParts[2] / 3600
                }
                this.clockingRemotes[i] = remoteHours
            }
        })
    }

    setClocking(date, type, time) {
        this.post(`/clockings/${formatDate(date)}/`, {
            [type]: time
        }).then(res => {
            let row = this.clockings.querySelector(".clocking.total")
            let i = Math.floor((date - this.startDate) / DAY_MS)
            let cell = row.cells[i + 1]
            let minutes = +res.clocking.total
            let hours = minutes / 60
            this.clockingTotals[i] = hours
            cell.innerText = hours === 0 ? "" : Math.round(hours * 100) / 100
            this.updateTotals()
        })
    }

    updateTotals() {
        let totalClockings = this.clockingTotals.reduce((a, b) => a + b, 0)
        let totalProjects = this.totalProjects
        this.clockings.querySelector(".clocking.total").cells[this.nDays + 1].innerText = Math.round(totalClockings * 100) / 100

        let startI = 0
        Array.from(this.clockings.querySelector(".clocking.remote-total").cells).forEach((cell, i) => {
            let endI = startI + cell.colSpan
            let remote = this.clockingRemotes.slice(startI, endI).reduce((a, b) => a + b, 0)
            let hour = Math.floor(remote)
            let min = Math.floor((remote - hour) * 60)
            cell.innerText = hour.toString().padStart(2, "0") + ":" + min.toString().padStart(2, "0")
            startI = endI
        })

        startI = 0
        Array.from(this.clockings.querySelector(".clocking.total2").cells).forEach((cell, i) => {
            let endI = startI + cell.colSpan
            let total = this.clockingTotals.slice(startI, endI).reduce((a, b) => a + b, 0)
            cell.innerText = Math.round(total * 100) / 100
            startI = endI
        })

        if (totalClockings === 0) {
            console.log("Total clockings = 0")
            return
        }
        if (totalProjects === 0) {
            console.log("Total projects = 0")
            return
        }
        this.times.querySelectorAll(".project.parent.productive").forEach(parent => {
            let total = +parent.dataset.total
            let workingTimeRatio = total / totalClockings
            let imputedTimeRatio = total / totalProjects
            parent.cells[this.nDays + 3].innerText = formatPercentage(workingTimeRatio)
            parent.cells[this.nDays + 4].innerText = formatPercentage(imputedTimeRatio)
            let sagexTime = imputedTimeRatio * totalClockings
            parent.cells[this.nDays + 5].innerText = Math.round(sagexTime * 100) / 100
        })
    }

    post(endpoint, data) {
        let fd = new FormData()
        Object.entries(data).forEach(([key, value]) => {
            fd.set(key, value)
        })
        return req(endpoint, {
            method: "POST",
            body: fd
        }).then(res => {
            return res.json()
        })
    }
}

window.addEventListener("load", () => {
    prevMonthBtn = document.getElementById("prev-month")
    nextMonthBtn = document.getElementById("next-month")
    month = document.getElementById("month")

    table = new Table(document.getElementById("table"))

    prevMonthBtn.addEventListener("click", () => table.prevMonth())
    nextMonthBtn.addEventListener("click", () => table.nextMonth())
})