(ns ajet.chat.api.channels-integration-test
  (:require [clojure.test :refer [deftest testing is use-fixtures]]
            [ajet.chat.api.handlers.channels :as channels]
            [ajet.chat.shared.db :as db]
            [ajet.chat.test-helpers :as th]))

(use-fixtures :once th/with-db-suite th/with-nats-suite)
(use-fixtures :each th/with-clean-db)

(defn- setup-community+admin
  "Create a community with an admin user. Returns [user community]."
  []
  (let [user (th/create-user!)
        comm (th/create-community!)
        _    (th/add-member! (:id comm) (:id user) {:role "admin"})]
    [user comm]))

(defn- setup-community+member
  "Create a community with a regular member. Returns [user community]."
  []
  (let [user (th/create-user!)
        comm (th/create-community!)
        _    (th/add-member! (:id comm) (:id user) {:role "member"})]
    [user comm]))

;;; ---------------------------------------------------------------------------
;;; Create channel
;;; ---------------------------------------------------------------------------

(deftest create-channel-admin-success
  (let [[user comm] (setup-community+admin)
        req  (th/mock-request {:method      :post
                               :user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:cid (str (:id comm))}
                               :body-params {:name "dev"}})
        resp (channels/create-channel req)
        body (th/response-body resp)]
    (is (= 201 (:status resp)))
    (is (= "dev" (:name body)))
    (is (= "public" (:visibility body)))))

(deftest create-channel-member-forbidden
  (let [[user comm] (setup-community+member)
        req  (th/mock-request {:method      :post
                               :user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:cid (str (:id comm))}
                               :body-params {:name "nope"}})]
    (is (= 403 (:status (th/with-exception-handler channels/create-channel req))))))

(deftest create-channel-blank-name-returns-422
  (let [[user comm] (setup-community+admin)
        req  (th/mock-request {:method      :post
                               :user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:cid (str (:id comm))}
                               :body-params {:name ""}})]
    (is (= 422 (:status (th/with-exception-handler channels/create-channel req))))))

(deftest create-channel-duplicate-name-returns-409
  (let [[user comm] (setup-community+admin)
        _    (th/create-channel! (:id comm) {:name "existing"})
        req  (th/mock-request {:method      :post
                               :user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:cid (str (:id comm))}
                               :body-params {:name "existing"}})]
    (is (= 409 (:status (th/with-exception-handler channels/create-channel req))))))

(deftest create-private-channel
  (let [[user comm] (setup-community+admin)
        req  (th/mock-request {:method      :post
                               :user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:cid (str (:id comm))}
                               :body-params {:name "secret" :visibility "private"}})
        resp (channels/create-channel req)
        body (th/response-body resp)]
    (is (= 201 (:status resp)))
    (is (= "private" (:visibility body)))))

;;; ---------------------------------------------------------------------------
;;; List channels
;;; ---------------------------------------------------------------------------

(deftest list-channels-returns-public-channels
  (let [[user comm] (setup-community+member)
        _    (th/create-channel! (:id comm) {:name "general"})
        _    (th/create-channel! (:id comm) {:name "secret" :visibility "private"})
        req  (th/mock-request {:user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:cid (str (:id comm))}})
        resp (channels/list-channels req)
        body (th/response-body resp)]
    (is (= 200 (:status resp)))
    (is (= 1 (count body)))
    (is (= "general" (:name (first body))))))

;;; ---------------------------------------------------------------------------
;;; Get channel
;;; ---------------------------------------------------------------------------

(deftest get-channel-member-success
  (let [[user comm] (setup-community+member)
        ch   (th/create-channel! (:id comm) {:name "test-ch"})
        _    (th/join-channel! (:id ch) (:id user))
        req  (th/mock-request {:user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:id (str (:id ch))}})
        resp (channels/get-channel req)]
    (is (= 200 (:status resp)))))

(deftest get-channel-non-member-private-returns-403
  (let [[user comm] (setup-community+member)
        ch   (th/create-channel! (:id comm) {:name "private-ch" :visibility "private"})
        req  (th/mock-request {:user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:id (str (:id ch))}})]
    (is (= 403 (:status (th/with-exception-handler channels/get-channel req))))))

;;; ---------------------------------------------------------------------------
;;; Join / Leave
;;; ---------------------------------------------------------------------------

(deftest join-public-channel-success
  (let [[user comm] (setup-community+member)
        ch   (th/create-channel! (:id comm) {:name "join-me"})
        req  (th/mock-request {:method      :post
                               :user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:id (str (:id ch))}})
        resp (channels/join-channel req)
        body (th/response-body resp)]
    (is (= 200 (:status resp)))
    (is (= "joined" (:status body)))))

(deftest join-private-channel-forbidden
  (let [[user comm] (setup-community+member)
        ch   (th/create-channel! (:id comm) {:name "private" :visibility "private"})
        req  (th/mock-request {:method      :post
                               :user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:id (str (:id ch))}})]
    (is (= 403 (:status (th/with-exception-handler channels/join-channel req))))))

(deftest leave-channel-success
  (let [[user comm] (setup-community+member)
        ch   (th/create-channel! (:id comm))
        _    (th/join-channel! (:id ch) (:id user))
        req  (th/mock-request {:method      :post
                               :user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:id (str (:id ch))}})
        resp (channels/leave-channel req)
        body (th/response-body resp)]
    (is (= 200 (:status resp)))
    (is (= "left" (:status body)))))

;;; ---------------------------------------------------------------------------
;;; Delete channel
;;; ---------------------------------------------------------------------------

(deftest delete-channel-admin-success
  (let [[user comm] (setup-community+admin)
        ch   (th/create-channel! (:id comm))
        req  (th/mock-request {:method      :delete
                               :user-id     (str (:id user))
                               :system      (th/test-system)
                               :path-params {:id (str (:id ch))}})
        resp (channels/delete-channel req)]
    (is (= 204 (:status resp)))))
