@startuml StudioAggregator
enum PaymentType {
  ORDER
  SUBSCRIPTION
}
enum Reason {
  TIMEOUT
  REJECTED
}

interface User {
  - getName()     :   String
  - getMobile()   :   int
}
class Studio implements User{
  <<constructor>> Studio(ID: String)  : void
  - studioID                       : string
  - historicalOrders               : HistoricalOrdersService(studioID)
  - homePageURL                    : String
  - orderPlacingService            :   OrderPlacingService()

  + getName()         :   String
  + getMobile()       :   int
  + getHomePageURL()  :   String
}
class Customer implements User{
  <<constructor>> Customer(ID: String)    : void

  - customerID                      : string
  - historicalOrders                : HistoricalOrdersService(customerID)
  - orderPlacingService   :   OrderPlacingService()

  + getName()         :   String
  + getMobile()       :   int
}
class StudioManagementService {
  + StudioID: String
}



class PaymentService {
  <<constructor>> PaymentService(): void
  - InternalPaymentID: String
  + SubscriptionID: String
  + Amount: int
  - PaymentStatus: enum (Success, Failure)
  - mysql               : MySQL(db_name: String)
  - walletService :   WalletService()

  + processPayment(orderID: string, paymentType: PaymentType, amount: float): PaymentStatus
  + retry(): void
  + refundPayment(orderID: string): void
}



class KafkaPublisher {
  + publishToKafka(): void
}

class SubscriptionService{
  - SubscriptionID: String
  - StudioID: String
  + StartDate: date
  - ExpiryDate: date
  - paymentService: PaymentService
  - kafkaPublisher: KafkaPublisher
  - tenureStrategy: SubscriptionTenureStrategy
  - notify : SubscriptionNotificationObserver
  - SubscriptionStatus: enum (Active, Expired)
  + getSubscriptionStatus(): SubscriptionStatus
  + getStartAndExpiryDate(): date
  + setStartAndExpiryDate(): date
  + extendSubscription(): bool


}
interface SubscriptionTenureStrategy {
  + calculateExpiryDate(): date
}
class MonthlySubscription implements SubscriptionTenureStrategy {
  + calculateExpiryDate(): date
}
class YearlySubscription implements SubscriptionTenureStrategy {
  + calculateExpiryDate(): date
}

interface NotificationObserver {
  + sendNotification(): void
}
class SubscriptionNotificationObserver implements NotificationObserver {
  + sendNotification(): void
}

class HistoricalOrdersService {
  - userID    : String
  - query     : String
  - cassandra : Cassandra(db_name: String)
  - queryGenerationStrategy      : QueryGenerationStrategy

  <<constructor>> HistoricalOrdersService(ID : String, strategy: QueryGenerationStrategy)  : void
  + getOrders(year: int)                  : Order[]
}


enum OrderStatus {
  - PLACED
  - ACCEPTED
  - REJECTED
  - EDITING
  - PRINTING
  - WAITING_FOR_DISPATCH
  - DISPATCHED
  - DELIVERED
  - CANCELLED
}
class Order {
  - orderId             : String
  - customerID          : String
  - studioID            : String
  - serviceID           : String

  - dateOfCreation      : Date
  - dateOfCompletion    : Date
  - status              : String
  - amount              : float
  - paymentMethod       : String

  - pictureS3Url        : String
  - invoiceURL          : String
  - eventAddress        : String
  - billingAddress      : String

  - mysql               : MySQL(db_name: String)
  - redis               : Redis(db_name: String)
  - studio              : Studio(studioID: String)
  - customer            : Customer(customerID: String)

  <<constructor>> Order(ID)             : void

  + getserviceSummary() : String
  + getStatus()         : String
  + setStatus(status: OrderStatus)         : boolean
  + getdateOfCreation() : Date
  + updateStatus()      : String
  + getAmount()         : float
  + getPaymentMethod()  : String
  + getPictureS3Url()   : String
  + getInvoiceURL()     : String
  + getEventAddress()   : String
  + getBillingAddress() : String
  + setEventAddress(address: String)      : boolean
  + setBillingAddress(address: String)    : boolean
  + requestCancellation(reason: String)   : boolean

}
class OrderArchivalService {
  - orderProcessingService_obj  :   OrderProcessingService()
  - cassandra_obj               :   Cassandra(db_name: String)

  <<constructor>> OrderArchivalService()             : void
  + run() :   void
}

class OrderProcessingService implements NotificationObserver {
  <<constructor>> OrderProcessingService(status: List<OrderStatus>)             : void

  - mysql               : MySQL(db_name: String)
  - redis               : Redis(db_name: String)
  - kafka               : KafkaPublisher

  + fetchOrdersByStatus(status: List<OrderStatus>) : Dataframe<Order>
  + deleteOrders(OrderIDs: List) : void

  - readOrdersFromRedis(year: int): Dataframe<Order>
  - readOrdersFromMySQL(year: int): Dataframe<Order>

  + viewOrderDetails(orderID: String): Order
  - readOrderDetailsFromRedis(orderID: String): Order
  - readOrderDetailsFromMySQL(orderID: String): Order

  + setOrderStatus(orderID: String, status: OrderStatus): boolean
  - setOrderStatusInRedis(orderID: String, status: OrderStatus): boolean
  - setOrderStatusInMySQL(orderID: String, status: OrderStatus): boolean

  + deleteOrders(orderIDs: List<orderID>): void
  - deleteFromRedis(orderIDs: List<orderID>): void
  - deleteFromMySQL(orderIDs: List<orderID>): void

  - sendNotification(order: Order): void
}
class OrderPlacingService implements NotificationObserver {
  <<constructor>> OrderPlacingService(): void
  - mysql               : MySQL(db_name: String)
  - paymentService    :   PaymentService()
  - generateOrderID(): string

  + initiate(customerID: string, studioID: string, serviceID: string, amount: float, eventAddress: string, shippingAddress: string): bool
  + cancel(orderIDs: List<string>, reason: Reason)  : void
  + confirmOrder(orderID: string): void
  + rejectOrder(): void
  + refundPayment(): void
  + sendNotification(message: string): void
}
class OrderTimeOutService{
  <<constructor>> OrderConfirmationService(): void

  - mysql               : MySQL(db_name: String)
  - orderPlacingService   :   OrderPlacingService()

  - getPlacedOrdersAndTime(): List<Order>
  + run()
}
class Database {
  - static getInstance(): Database
  + connect()                   : Session
  + disconnect()                : void
  + executeQuery(query: String) : ResultSet
}
class MySQL implements Database {
  <<Singleton>> - conn_string: Session
  - static instance: MySQL

  <<constructor>> MySQL(db_name: String)  : void
  - static getInstance(): MySQL
  + connect()                   : Session
  + disconnect()                : void
  + executeQuery(query: String) : ResultSet
  + insertRow(tableName: String, row: dict)   : void
}
class Redis implements Database {
  - conn_string: Session
  - static instance: Redis

  <<constructor>> Redis(db_name: String)  : void
  - static getInstance(): MySQL
  + connect()                   : Session
  + disconnect()                : void
  + executeQuery(query: String) : ResultSet
  + insertRows(tableName: string, df: Dataframe): void
}
class Cassandra implements Database {
  - conn_string: Session
  - static instance: Cassandra

  <<constructor>> Cassandra(db_name: String)  : void
  + static getInstance(): Cassandra
  + connect()                   : Session
  + disconnect()                : void
  + executeQuery(query: String) : ResultSet
  + insertRows(tableName: String, data: Map<String, object>): void

}


class ResultSet {
  + next(): boolean
}
class ReferralService{
    - walletService :   WalletService()
}
class WalletService {
    <<constructor>> WalletService() :   void
    - mysql               : MySQL(db_name: String)

    + viewBalance(userID: String): int
    + viewTransactionHistory(userID: String, (0,30)/(31,60): range): List<Transaction>
    + addMoney(userID: String, amount: int, comments: String): bool
    + redeem(userID: String, amount: int, comments: String): bool
}
class Transaction {
    - transactionID: String
    - userID: String
    - amount: double
    - comments: String
    - date: Date
}
class Service {
    - serviceID: int
    - name: String
    - summary: String
    - price: Double
    - minDurationForDeliveryInDays: int
    - maxDurationForDeliveryInDays: int
}

interface WishlistAndCartService {
    + addTo(StudioID: int, serviceId: int): bool
    + deleteFrom(StudioID: int, serviceId: int): bool
    + view(): List<Service>
}
class Wishlist implements WishlistAndCartService {
    <<constructor>> Wishlist(userID: int)

    - userID: int
    - kafkaPublisher: KafkaPublisher
    - mysql         : MySQL(db_name: String)

    + addTo(StudioID: int, serviceId: int): bool
    + deleteFrom(StudioID: int, serviceId: int): bool
    + view(): List<Service>
}
class Cart implements WishlistAndCartService {
    <<constructor>> Wishlist(userID: int)

    - userID: int
    - kafkaPublisher: KafkaPublisher
    - mysql         : MySQL(db_name: String)

    + addTo(StudioID: int, serviceId: int): bool
    + deleteFrom(StudioID: int, serviceId: int): bool
    + view(): List<Service>
}

Studio   "1" -- "*" Order   : has-many
Customer "1" -- "*" Order   : has-many
Wishlist "1" -- "*" Service : has-many
Cart "1" -- "*" Service     : has-many

SubscriptionService     *-- KafkaPublisher                    : Has-a
OrderProcessingService  *-- KafkaPublisher                    : Has-a
OrderPlacingService *-- Reason                                : has-a
OrderArchivalService *-- OrderProcessingService               : has-a
OrderArchivalService *-- Cassandra                            : has-a
SubscriptionService     *--  PaymentService                   : Has-a
SubscriptionService     *-- SubscriptionNotificationObserver  : Has-a
StudioManagementService *-- SubscriptionService               : Has-a
SubscriptionTenureStrategy --* SubscriptionService            : Has-a
Studio    *-- HistoricalOrdersService                         : has-a
Customer  *-- HistoricalOrdersService                         : has-a
HistoricalOrdersService --> Cassandra                         : has-a
Cassandra --> ResultSet                                       : has-a
Studio "1" *-- "0..*" Order                                   : has-a
Customer "1" *-- "0..*" Order                                 : has-a
PaymentService      *-- PaymentType                           : has-a
User *-- Wishlist                                             : has-a
User *-- Cart                                                 : has-a

HistoricalOrdersService ..> QueryGenerationStrategy : uses
Order -- MySQL                                      : uses
Order -- Redis                                      : uses
OrderPlacingService -down-> PaymentService          : uses
OrderPlacingService <-- OrderTimeOutService         : uses
OrderPlacingService <-up- Studio                    : uses
OrderPlacingService <-up- Customer                  : uses
OrderProcessingService --> Order                    : uses
OrderArchivalService --> Order                      : uses
OrderPlacingService --> MySQL                       : uses
OrderTimeOutService --> MySQL                       : uses
ReferralService --|> WalletService                  : uses
PaymentService --|> WalletService                   : uses
WalletService --|> MySQL                            : uses

Transaction --|> WalletService  : relates



OrderArchivalService --> "Cron Job"

@enduml
