use sigil_stitch::code_block::{CodeBlock, StringLitArg};
use sigil_stitch::lang::swift::Swift;
use sigil_stitch::spec::field_spec::FieldSpec;
use sigil_stitch::spec::file_spec::FileSpec;
use sigil_stitch::spec::fun_spec::{FunSpec, TypeParamSpec};
use sigil_stitch::spec::modifiers::{TypeKind, Visibility};
use sigil_stitch::spec::parameter_spec::ParameterSpec;
use sigil_stitch::spec::type_spec::TypeSpec;
use sigil_stitch::type_name::TypeName;
fn main() {
let url = TypeName::importable("Foundation", "URL");
let data = TypeName::importable("Foundation", "Data");
let json_decoder = TypeName::importable("Foundation", "JSONDecoder");
let publisher = TypeName::importable("Combine", "AnyPublisher");
let url_session = TypeName::importable("Foundation", "URLSession");
let priority = TypeSpec::builder("Priority", TypeKind::Enum);
let priority = priority.visibility(Visibility::Public);
let priority = priority.doc("Task priority levels.");
let mut cases = CodeBlock::builder();
cases.add("case low", ());
cases.add_line();
cases.add("case medium", ());
cases.add_line();
cases.add("case high", ());
cases.add_line();
cases.add("case critical", ());
cases.add_line();
let priority = priority.extra_member(cases.build().unwrap());
let priority_spec = priority.build().unwrap();
let tp = TypeParamSpec::new("T");
let repo_spec = TypeSpec::builder("TaskRepository", TypeKind::Interface)
.add_type_param(tp)
.doc("Repository for task persistence.")
.doc("")
.doc("- Parameter T: the task entity type")
.add_method(
FunSpec::builder("findById")
.returns(TypeName::primitive("T?"))
.add_param(ParameterSpec::new("id", TypeName::primitive("String")).unwrap())
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("findAll")
.returns(TypeName::primitive("[T]"))
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("save")
.add_param(ParameterSpec::new("entity", TypeName::primitive("T")).unwrap())
.build()
.unwrap(),
)
.build()
.unwrap();
let task_spec = TypeSpec::builder("Task", TypeKind::Struct)
.visibility(Visibility::Public)
.doc("A task entity.")
.add_field(
FieldSpec::builder("id", TypeName::primitive("String"))
.visibility(Visibility::Public)
.is_readonly()
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("name", TypeName::primitive("String"))
.visibility(Visibility::Public)
.is_readonly()
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("priority", TypeName::primitive("Priority"))
.visibility(Visibility::Public)
.is_readonly()
.build()
.unwrap(),
)
.add_field(
FieldSpec::builder("completed", TypeName::primitive("Bool"))
.visibility(Visibility::Public)
.initializer(CodeBlock::of("false", ()).unwrap())
.build()
.unwrap(),
)
.build()
.unwrap();
let base_svc = TypeSpec::builder("BaseService", TypeKind::Class);
let base_svc = base_svc.visibility(Visibility::Public);
let base_svc = base_svc.doc("Base class for services with logging.");
let base_svc = base_svc.add_field(
FieldSpec::builder("serviceName", TypeName::primitive("String"))
.visibility(Visibility::Public)
.is_readonly()
.build()
.unwrap(),
);
let log_body = CodeBlock::of("print(\"[\\(serviceName)] \\(message)\")", ()).unwrap();
let base_svc = base_svc.add_method(
FunSpec::builder("log")
.add_param(ParameterSpec::new("message", TypeName::primitive("String")).unwrap())
.body(log_body)
.build()
.unwrap(),
);
let base_svc_spec = base_svc.build().unwrap();
let task_svc = TypeSpec::builder("TaskService", TypeKind::Class);
let task_svc = task_svc.visibility(Visibility::Public);
let task_svc = task_svc.extends(TypeName::primitive("BaseService"));
let task_svc = task_svc.extends(TypeName::primitive("TaskRepository"));
let task_svc = task_svc.doc("Task management service.");
let task_svc = task_svc.add_field(
FieldSpec::builder("tasks", TypeName::primitive("[Task]"))
.visibility(Visibility::Private)
.initializer(CodeBlock::of("[]", ()).unwrap())
.build()
.unwrap(),
);
let find_body = CodeBlock::of("return tasks.first { $0.id == id }", ()).unwrap();
let task_svc = task_svc.add_method(
FunSpec::builder("findById")
.returns(TypeName::primitive("Task?"))
.add_param(ParameterSpec::new("id", TypeName::primitive("String")).unwrap())
.body(find_body)
.build()
.unwrap(),
);
let find_all_body = CodeBlock::of("return tasks", ()).unwrap();
let task_svc = task_svc.add_method(
FunSpec::builder("findAll")
.returns(TypeName::primitive("[Task]"))
.body(find_all_body)
.build()
.unwrap(),
);
let save_body = CodeBlock::of("tasks.append(entity)", ()).unwrap();
let task_svc = task_svc.add_method(
FunSpec::builder("save")
.add_param(ParameterSpec::new("entity", TypeName::primitive("Task")).unwrap())
.body(save_body)
.build()
.unwrap(),
);
let task_svc_spec = task_svc.build().unwrap();
let fetch_body = CodeBlock::of(
"let (responseData, _) = try await %T.shared.data(from: endpoint)\nlet decoder = %T()\nreturn try decoder.decode([Task].self, from: responseData)",
(url_session, json_decoder),
)
.unwrap();
let fetch_tasks = FunSpec::builder("fetchTasks")
.visibility(Visibility::Public)
.is_async()
.returns(TypeName::primitive("[Task]"))
.add_param(ParameterSpec::new("endpoint", TypeName::primitive("URL")).unwrap())
.body(fetch_body)
.build()
.unwrap();
let create_body = CodeBlock::of(
"guard let url = %T(string: urlString) else {\n fatalError(%S)\n}\nreturn url",
(url, StringLitArg("Invalid URL".to_string())),
)
.unwrap();
let make_url = FunSpec::builder("makeURL")
.returns(TypeName::primitive("URL"))
.add_param(ParameterSpec::new("urlString", TypeName::primitive("String")).unwrap())
.body(create_body)
.build()
.unwrap();
let combine_trigger = CodeBlock::of("// Publisher: %T", (publisher,)).unwrap();
let data_trigger = CodeBlock::of("// Data: %T", (data,)).unwrap();
let file = FileSpec::builder_with("TaskApp.swift", Swift::new())
.add_code(combine_trigger)
.add_code(data_trigger)
.add_type(priority_spec)
.add_type(repo_spec)
.add_type(task_spec)
.add_type(base_svc_spec)
.add_type(task_svc_spec)
.add_function(fetch_tasks)
.add_function(make_url)
.build()
.unwrap();
let output = file.render(80).unwrap();
print!("{output}");
}