;; SPDX-FileCopyrightText: 2026 Andrew Oberstar <andrew@ajoberstar.org>
;; SPDX-License-Identifier: AGPL-3.0-only

(in-package #:org.ajoberstar.quixote.model)

(defclass entity ()
  ((id :initarg :id :type integer :reader entity-id)
   (created-at :initarg :created-at :type dates:date :reader entity-created-at)
   (updated-at :initarg :updated-at :type dates:date :accessor entity-updated-at)))

(defgeneric book-id (book))
(defgeneric book-title (book))
(defgeneric book-author (book))
(defgeneric book-year (book))
(defgeneric book-type (book))
(defgeneric book-pages (book))
(defgeneric book-isbn (book))

(defclass book (entity)
  ((title :initarg :title :type string :accessor book-title)
   (author :initarg :author :type string :accessor book-author)
   (year :initarg :year :type integer :accessor book-year)
   (type :initarg :type :type string :accessor book-type)
   (pages :initarg :pages :type integer :accessor book-pages)
   (isbn :initarg :isbn :type string :accessor book-isbn)))

(defun make-book (title author year type pages isbn)
  (make-instance 'book
                 :id nil
                 :created-at nil
                 :updated-at nil
                 :title title
                 :author author
                 :year year
                 :type type
                 :pages pages
                 :isbn isbn))

(defmethod print-object ((obj book) stream)
  (print-unreadable-object (obj stream :type t)
    (format stream "~A (~A, ~:d p.)"
            (book-title obj)
            (or (book-author obj) "Unknown")
            (book-pages obj))))

(defmethod book-id ((book book))
  (entity-id book))

(defgeneric readthrough-id (readthrough))
(defgeneric readthrough-book (readthrough))
(defgeneric readthrough-started-at (readthrough))
(defgeneric readthrough-completed-at (readthrough))

(defclass readthrough (entity)
  ((book :initarg :book :type book :accessor readthrough-book)
   (started-at :initarg :started-at :type dates:date :accessor readthrough-started-at)
   (completed-at :initarg :completed-at :type dates:date :accessor readthrough-completed-at)))

(defun make-readthrough (book started-at completed-at)
  (make-instance 'readthrough
                 :id nil
                 :created-at nil
                 :updated-at nil
                 :book book
                 :started-at started-at
                 :completed-at completed-at))

(defmethod print-object ((obj readthrough) stream)
  (flet ((write-date (date)
           (if date (dates:write-iso-date nil date) "???")))
    (print-unreadable-object (obj stream :type t)
      (format stream "~A (~A - ~A)"
              (book-title (readthrough-book obj))
              (write-date (readthrough-started-at obj))
              (write-date (readthrough-completed-at obj))))))

(defmethod readthrough-id ((readthrough readthrough))
  (entity-id readthrough))

(defmethod book-id ((book readthrough))
  (entity-id (readthrough-book book)))

(defmethod book-title ((book readthrough))
  (book-title (readthrough-book book)))

(defmethod book-author ((book readthrough))
  (book-author (readthrough-book book)))

(defmethod book-year ((book readthrough))
  (book-year (readthrough-book book)))

(defmethod book-type ((book readthrough))
  (book-type (readthrough-book book)))

(defmethod book-pages ((book readthrough))
  (book-pages (readthrough-book book)))

(defmethod book-isbn ((book readthrough))
  (book-isbn (readthrough-book book)))

(defgeneric reading-id (reading))
(defgeneric reading-readthrough (reading))
(defgeneric reading-at (reading))
(defgeneric reading-page-start (reading))
(defgeneric reading-page-end (reading))

(defclass reading (entity)
  ((readthrough :initarg :readthrough :type readthrough :accessor reading-readthrough)
   (at :initarg :at :type dates:date :accessor reading-at)
   (page-start :initarg :page-start :type integer :accessor reading-page-start)
   (page-end :initarg :page-end :type integer :accessor reading-page-end)))

(defun make-reading (readthrough at page-start page-end)
  (make-instance 'reading
                 :id nil
                 :created-at nil
                 :updated-at nil
                 :readthrough readthrough
                 :at at
                 :page-start page-start
                 :page-end page-end))

(defmethod print-object ((obj reading) stream)
  (flet ((write-date (date)
           (if date (dates:write-iso-date nil date) "???")))
    (print-unreadable-object (obj stream :type t)
      (format stream "~A (~A, pp. ~:d-~:d)"
              (book-title (readthrough-book (reading-readthrough obj)))
              (write-date (reading-at obj))
              (reading-page-start obj)
              (reading-page-end obj)))))

(defmethod reading-id ((reading reading))
  (entity-id reading))

(defun reading-pages (reading)
  (declare (type reading reading))
  (- (reading-page-end reading)
     (reading-page-start reading)))

(defmethod book-id ((book reading))
  (entity-id (readthrough-book (reading-readthrough book))))

(defmethod book-title ((book reading))
  (book-title (readthrough-book (reading-readthrough book))))

(defmethod book-author ((book reading))
  (book-author (readthrough-book (reading-readthrough book))))

(defmethod book-year ((book reading))
  (book-year (readthrough-book (reading-readthrough book))))

(defmethod book-type ((book reading))
  (book-type (readthrough-book (reading-readthrough book))))

(defmethod book-pages ((book reading))
  (book-pages (readthrough-book (reading-readthrough book))))

(defmethod book-isbn ((book reading))
  (book-isbn (readthrough-book (reading-readthrough book))))

(defmethod readthrough-id ((readthrough reading))
  (entity-id (reading-readthrough readthrough)))

(defmethod readthrough-started-at ((readthrough reading))
  (readthrough-started-at (reading-readthrough readthrough)))

(defmethod readthrough-completed-at ((readthrough reading))
  (readthrough-completed-at (reading-readthrough readthrough)))
