(nsv2(:require[clojure.spec.alpha:ass][clojure.string:asstr]))(s/def::distance(s/andnumber?pos?))(s/def::packagestring?)(s/def::destinationstring?)(s/def::delivery-type#{"air""land""sea"})(defprotocolDeliveryService(calculate-cost[thisdistance]"Calculates the delivery cost based on the distance.")(process-delivery[thispackagedestination]"Executes the delivery of the package to the destination.")(estimated-time[thisdistance]"Estimates delivery time in hours."))(defrecordAirDelivery[]DeliveryService(calculate-cost[_distance](*5distance))(process-delivery[_packagedestination](str"Package '"package"' will be delivered via Air to "destination))(estimated-time[_distance](Math/ceil(/distance800))))(defrecordLandDelivery[]DeliveryService(calculate-cost[_distance](*2distance))(process-delivery[_packagedestination](str"Package '"package"' will be delivered via Land to "destination))(estimated-time[_distance](Math/ceil(/distance60))))(defrecordSeaDelivery[]DeliveryService(calculate-cost[_distance](*1distance))(process-delivery[_packagedestination](str"Package '"package"' will be delivered via Sea to "destination))(estimated-time[_distance](Math/ceil(/distance30))))(defmulticreate-delivery-service"Factory multi-method for creating delivery services"(fn[type&_](str/lower-casetype)))(defmethodcreate-delivery-service"air"[_](->AirDelivery))(defmethodcreate-delivery-service"land"[_](->LandDelivery))(defmethodcreate-delivery-service"sea"[_](->SeaDelivery))(defmethodcreate-delivery-service:default[type](throw(ex-info"Invalid delivery type"{:typetype:available-types#{"air""land""sea"}})))(defncalculate-and-deliver"Service that uses the factory to calculate the cost and deliver a package.
Returns a map with :cost, :delivery, and :estimated-time keys."[typepackagedestinationdistance]{:pre[(s/valid?::delivery-typetype)(s/valid?::packagepackage)(s/valid?::destinationdestination)(s/valid?::distancedistance)]}(try(let[service(create-delivery-servicetype)]{:cost(calculate-costservicedistance):delivery(process-deliveryservicepackagedestination):estimated-time(estimated-timeservicedistance)})(catchExceptione(throw(ex-info"Delivery calculation failed"{:cause(.getMessagee):typetype:packagepackage:destinationdestination:distancedistance})))))(defnfind-cheapest-delivery"Finds the cheapest delivery service for given parameters"[packagedestinationdistance](->>["air""land""sea"](map#(->[(calculate-and-deliver%packagedestinationdistance)%]))(sort-by(comp:costfirst))first))(comment(calculate-and-deliver"air""Electronics""São Paulo"1000);; => {:cost 5000;; :delivery "Package 'Electronics' will be delivered via Air to São Paulo";; :estimated-time 2}(find-cheapest-delivery"Heavy Machinery""Porto Alegre"800);; => [{:cost 800;; :delivery "Package 'Heavy Machinery' will be delivered via Sea to Porto Alegre";; :estimated-time 27};; "sea"];; Validation error example(calculate-and-deliver"air""Books""Curitiba"-100);; => Assertion Error: Invalid input)