;; testnet
;; (impl-trait .nft-trait.nft-trait)
;; mainnet
(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait)

(define-non-fungible-token groober-city uint)

;; ERRORS
(define-constant ERR-NOT-CONTRACT-OWNER (err u100))
(define-constant ERR-NOT-TOKEN-OWNER (err u101))
(define-constant ERR-ALL-MINTED (err u102))
(define-constant ERR-INSUFFICENT-FUNDS (err u103))
(define-constant ERR-STX-TRANSFER (err u104))
(define-constant ERR-NFT-MINT (err u105))
(define-constant ERR-MINT-PAUSED (err u106))
(define-constant ERR-NOT-AUTHORIZED (err u107))

;; CONSTANTS
(define-constant CONTRACT-OWNER tx-sender)
(define-constant TOKEN-LIMIT u400)
(define-constant URI-CID "QmbGWLCXAT7fGDoUa7ad43RLUmtbtcGmCcnYKgCbuBnndp/")

;; VARS
(define-data-var last-token-id uint u0)
(define-data-var mint-price uint u30000000)
(define-data-var is-paused bool false)

;; testnet
;; (define-data-var artist-address principal 'ST1CSHTKVHMMQJ7PRQRFYW6SB4QAW6SR3XZ54PKG7)
;; mainnet
(define-data-var artist-address principal 'SPJAPA3VKBC4CJBGG2B3N6GSW5AWPVFV3R5FRBM1)

;; testnet
;; (define-data-var helper-address principal 'ST1B9MPJCA78KRFP2M312N8KFFGZR33HB6CZ6HJ0X)
;; mainnet
(define-data-var helper-address principal 'SP1B9MPJCA78KRFP2M312N8KFFGZR33HB6EJH1NHQ)

;; READ-ONLY
(define-read-only (get-last-token-id) 
    (ok (var-get last-token-id))
)

(define-read-only (get-owner (token-id uint)) 
    (ok (nft-get-owner? groober-city token-id))
)

(define-read-only (get-token-uri (token-id uint)) 
    ;; (ok (some (concat (concat (concat "ipfs://" URI-CID) (unwrap-panic (element-at LOOKUP token-id))) ".json")))
    (ok (some (concat (concat (concat "ipfs://" URI-CID) (uint-to-string token-id)) ".json")))
)

(define-read-only (get-mint-price)
    (ok (var-get mint-price))
)

(define-read-only (get-is-paused)
    (ok (var-get is-paused))
)

;; PUBLIC
(define-public (transfer (token-id uint) (sender principal) (recipient principal))
    (begin 
        (asserts! (is-eq tx-sender sender) ERR-NOT-TOKEN-OWNER)
        (nft-transfer? groober-city token-id sender recipient)
    )
)

(define-public (burn (token-id uint))
    (begin 
        (asserts! (is-owner token-id tx-sender) ERR-NOT-TOKEN-OWNER)
        (nft-burn? groober-city token-id tx-sender)
    )
)

(define-public (claim)
    (mint tx-sender)
)

(define-public (pause-mint)
    (begin 
        (asserts! (or (is-eq tx-sender (var-get artist-address)) (is-eq tx-sender CONTRACT-OWNER)) ERR-NOT-AUTHORIZED)
        (ok (var-set is-paused true))
    )
)

(define-public (unpause-mint)
    (begin 
        (asserts! (or (is-eq tx-sender (var-get artist-address)) (is-eq tx-sender CONTRACT-OWNER)) ERR-NOT-AUTHORIZED)
        (ok (var-set is-paused false))
    )
)

;; PRIVATE
(define-private (mint (recipient principal))
    (let 
        (
            (token-id (+ (var-get last-token-id) u1))
            (price (var-get mint-price))
            (paused (var-get is-paused))
        )
        (asserts! (<= token-id TOKEN-LIMIT) (err ERR-ALL-MINTED))
        (asserts! (not paused) (err ERR-MINT-PAUSED))
        (unwrap! (stx-transfer? u28800000 tx-sender (var-get artist-address)) (err ERR-STX-TRANSFER))
        (unwrap! (stx-transfer? u1200000 tx-sender (var-get helper-address)) (err ERR-STX-TRANSFER))
        (unwrap! (nft-mint? groober-city token-id recipient) (err ERR-NFT-MINT))
        (var-set last-token-id token-id)
        (ok token-id)
    )
)

(define-private (is-owner (token-id uint) (user principal))
    (is-eq user (unwrap! (nft-get-owner? groober-city token-id) false))
)

;; CODE FOR UINT -> STRING
(define-constant FOLDS_3 (list true true true))

(define-constant NUM_TO_CHAR (list    
    "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"
))

(define-private (concat-uint (ignore bool) (input { dec: uint, data: (string-ascii 3) }))
    (let 
        (            
            (last-val (get dec input))        
        )        
        (if (is-eq last-val u0)            
            {                
                dec: last-val,                
                data: (get data input)            
            }            
            (if (< last-val u10)                
                {                    
                    dec: u0,                    
                    data: (concat-num-to-string last-val (get data input))                
                }                
                {                    
                    dec: (/ last-val u10),                    
                    data: (concat-num-to-string (mod last-val u10) (get data input))                
                }            
            )        
        )    
    )
)

(define-private (concat-num-to-string (num uint) (right (string-ascii 3)))    
    (unwrap-panic (as-max-len? (concat (unwrap-panic (element-at NUM_TO_CHAR num)) right) u3))
)

(define-private (uint-to-string (num uint))    
    (if (is-eq num u0)        
        (unwrap-panic (as-max-len? "0" u3))        
        (get data (fold concat-uint FOLDS_3 { dec: num, data: ""}))    
    )
)