شما باید یک سیستم شبیهسازی خودپرداز (*ATM*) طراحی کنید که بتواند تراکنشهای مالی شامل ثبتنام کاربر، ورود به سیستم، برداشت، واریز و انتقال وجه [و استعلام موجودی] را پردازش کند. علاوه بر آن، سیستم باید قادر به مدیریت و ثبت گزارش و [لاگ](https://en.wikipedia.org/wiki/Logging_%28computing%29)های مختلف باشد و امکان کوئری زدن روی این لاگها را فراهم کند.
در این سیستم، هر لاگ سه ویژگی سطح لاگ (`level`)، پیام (`message`) و زمان ایجاد (`timestamp`) دارد.
همچنین سه سطح لاگ قابل ثبت است. هرکدام از این نوع لاگها، در شرایطی قابل ثبت هستند و کاربردهای متفاوتی دارند.
### سطوح لاگها
1. **لاگ `ERROR` (خطا):**
+ مواردی که به دلیل مشکل جدی یا ناموفق بودن عملیات رخ دادهاند.
+ مثال: خطای `insufficient funds` یا `invalid credentials`.
2. **لاگ `INFO` (اطلاعات):**
+ اطلاعات عمومی مربوط به رخدادهای موفق و معمولی سیستم.
+ مثال: موفقیت در `login` یا `deposit`.
3. **لاگ `DEBUG` (دیباگ):**
+ اطلاعات دقیقتر و فنی برای ردیابی مشکلات یا رفتارهای سیستم.
+ مثال: `session created` یا `amount updated`.
### نشست (*Session*)
نشست (*Session*) در برنامههای کاربردی، بهویژه در سیستمهای تحت وب به دورهای از ارتباط فعال بین کاربر و سیستم گفته میشود که در آن اطلاعاتی موقت ذخیره و مدیریت میشود. نشستها معمولاً برای شناسایی کاربران، حفظ وضعیت تعامل آنها با سیستم، و مدیریت امنیت به کار میروند. هر نشست در این سیستم ویژگیهای زیر را دارد:
1. **شناسه یکتا (`Session ID`):**
+ یک نشست با یک شناسه یکتا مشخص میشود. در سیستم ما `Session ID` بهصورت یک شمارنده افزایشی (شروع از ۱) تولید میشود.
2. **مدت اعتبار (`Expiration`):**
+ نشستها موقتی هستند و تا زمانی معتبرند که کاربر با سیستم تعامل داشته باشد یا زمان انقضای مشخصشده به پایان نرسد.
+ در این سیستم، اگر از زمان ورود بیش از ۱۰ دقیقه (۶۰۰ ثانیه) گذشته باشد، نشست منقضی میشود.
+ زمان انقضای هر نشست تنها در هنگام اجرای دستورات بررسی میشود.
3. در این سیستم، هر کاربر تنها دارای یک نشست فعال است.
### دستورهای مجاز
1. `register <user> <password> <role> <timestamp>`
2. `login <user> <password> <timestamp>`
3. `logout <session> <timestamp>`
4. `withdraw <session> <amount> <timestamp>`
5. `deposit <session> <amount> <timestamp>`
6. `transfer <session> <target_user> <amount> <timestamp>`
7. `log <level> <starttime> <finishtime> <timestamp>`
هر یک از این دستورات، در واقع شبیهسازی انجام آنها در واقعیت است. در ادامه، عملکرد هر دستور را به طور کامل شرح میدهیم.
<details class="red">
<summary>**ثبتنام**</summary>
```
register <user> <password> <role> <timestamp>
```
این دستور کاربر جدیدی را با اطلاعات زیر ثبت میکند:
+ `<user>`: نام کاربری.
+ `<password>`: رمز عبور.
+ `<role>`: نقش کاربر که میتواند `"admin"` یا `"user"` باشد.
+ `<timestamp>`: زمان ثبتنام به فرمت `yyyy/MM/dd:HH:mm:ss`.
نام کاربری به ازای هر کاربر منحصر به فرد است. همچنین موجودی اولیه تمام کاربرانی که ثبتنام میکنند، در ابتدا صفر میباشد.
اگر کاربر قبلاً ثبت شده باشد، لاگ خطای `"user already registered"` از نوع `ERROR` ثبت شود و اطلاعات آن کاربر در سیستم ثبت **نشود**. در غیر اینصورت، لاگ اطلاعات `"user registered successfully"` از نوع `INFO` ثبت شود.
</details>
<details class="orange">
<summary>**ورود به حساب**</summary>
```
login <user> <password> <timestamp>
```
کاربر با نام کاربری و رمز عبور معتبر وارد سیستم میشود:
+ اگر نام کاربری یا رمز عبور نادرست باشد، لاگ خطای `"invalid credentials"` با نوع `ERROR` ثبت کنید.
+ اگر کاربر قبلاً وارد شده باشد، لاگ اطلاعات `"already logged in"` با نوع `INFO` ثبت کنید. سپس در صورتی که از نشست (`session`) قبلی بیش از ۱۰ دقیقه زمان گذشته باشد، یک نشست جدید برای آن کاربر ایجاد و نشست قبلی حذف شود. در غیر این صورت، تغییری در سیستم ایجاد نخواهد شد و کاربر وارد حسابش میشود.
+ در صورتی که هیچ یک از شرایط بالا برقرار نبود، یک نشست جدید ساخته شده، کاربر وارد حساب میشود و یک لاگ `INFO` با پیام `"user logged in successfully"` ثبت میشود.
**مکانیزم تولید `session ID`:**برای هر ورود موفق، یک شناسه یکتا (`session ID`) به کاربر اختصاص داده میشود. این شناسه در واقع یک شمارنده افزایشی با شروع از ۱ است و به ازای هر ورود کاربران، یکی افزایش پیدا میکند.
</details>
<details class="yellow">
<summary>**خروج از حساب**</summary>
```
logout <session ID> <timestamp>
```
این دستور کاربر را از سیستم خارج میکند و `session` مربوط به او را از حافظه حذف میکند:
+ اگر شناسه نشست معتبر نباشد یا منقضی شده باشد، لاگ `"session expired or invalid"` با نوع `ERROR` ثبت میشود.
+ در غیر این صورت لاگ `INFO` با پیام `"user logged out"` ثبت شود.
</details>
<details class="green">
<summary>**برداشت وجه**</summary>
```
withdraw <session ID> <amount> <timestamp>
```
این دستور به کاربر اجازه برداشت مبلغ مشخصی به اندازه `amoumt` از حساب را میدهد:
+ `<session>`: شناسه نشست کاربر.
+ `<amount>`: مبلغ برداشت.
+ `<timestamp>`: زمان درخواست.
**قوانین:**
+ اگر نشست منقضی شده باشد (حداقل **۱۰ دقیقه** از ورود گذشته باشد)، لاگ `"session expired"` با نوع `ERROR` ثبت شود.
+ **محاسبه انقضا:** از اختلاف زمانی بین `<timestamp>` اجرای دستور و زمان ورود (`login`) استفاده کنید. سیستم باید از فرمت زمانی `yyyy/MM/dd:HH:mm:ss` استفاده کند.
+ اگر موجودی کافی نباشد، لاگ`"insufficient funds"` با نوع `ERROR` ثبت میشود.
+ در صورت برداشت موفقیتآمیز، لاگ `INFO` با پیام `"amount withdrawn successfully"` ثبت شود.
</details>
<details class="teal">
<summary>**واریز وجه**</summary>
```
deposit <session ID> <amount> <timestamp>
```
مبلغ مشخصی را به حساب کاربر اضافه میکند.
+ اگر نشست منقضی شده باشد، لاگ `"session expired"` با نوع `ERROR` ثبت شود.
+ در غیر این صورت لاگ `INFO` با پیام `"amount deposited successfully"` ثبت شود.
</details>
<details class="blue">
<summary>**انتقال**</summary>
```
transfer <session ID> <target_user> <amount> <timestamp>
```
این دستور به کاربران اجازه میدهد مبلغی را از حساب خود به حساب کاربر دیگری انتقال دهند:
+ `<session>`: شناسه نشست کاربر فرستنده.
+ `<target_user>`: نام کاربری دریافتکننده.
+ `<amount>`: مبلغ انتقال.
+ `<timestamp>`: زمان درخواست.
**قوانین:**
1. اگر نشست منقضی شده باشد، لاگ `"session expired"` با نوع `ERROR` ثبت شود.
2. اگر کاربر دریافتکننده ثبت نشده باشد، لاگ `"target user not found"` با نوع `ERROR` ثبت کنید.
2. اگر موجودی کافی نباشد، خطای `"insufficient funds"` با نوع `ERROR` ثبت کنید.
3. شرایط بالا، از اول به آخر بررسی شوند و در صورتی که تناقضی در هر یک از شرایط به وجود بیاید، ادامه روند متوقف شود.
4. در صورت انتقال موفقیتآمیز، لاگ `INFO` با پیام `"amount transferred successfully"` ثبت شود.
</details>
<details class="purple">
<summary>**بررسی لاگها**</summary>
```
log <level> [<t1>] [<t2>] <timestamp>
```
این دستور برای کوئری زدن روی لاگهای سیستم استفاده میشود:
+ `<level>`: سطح لاگ که میتواند `error`، `info` یا `debug` باشد.
+ `<t1>` و `<t2>`: بازه زمانی که لاگها باید در آن جستجو شوند. این پارامترها اختیاری هستند؛ اگر `t1` و `t2` ارائه نشود، همه لاگهای موجود با سطح مشخصشده بدون توجه به زمان بازگردانده شوند.
+ `<timestamp>`: زمان ارسال درخواست برای اجرای دستور `log` است.
+ بلافاصله پس از اجرای دستور، یک لاگ `get log [level] [t1] [t2] [timestamp]` با نوع `DEBUG` در سیستم ثبت و در کنسول چاپ شود.
**خروجی:**
1. خروجی شامل تمام لاگهایی است که:
+ سطح لاگ آنها برابر با `<level>` است.
+ زمان ثبت لاگ در بازه `[<t1>, <t2>]` قرار دارد.
2. فرمت چاپ لاگها در کنسول به صورت زیر است:
```
<timestamp> <level> <message>
```
3. اگر لاگی پیدا نشود، خروجی `"no logs found"` در کنسول چاپ شود.
</details>
### نکات قابل توجه
1. پس از انجام هر نوع عملیات، لاگ آن در سیستم ثبت میشود. همچنین پس از ثبت لاگ، خروجی آن با فرمت
```
[log-type] log-message
```
در کنسول چاپ میشود.
2. هر دستور، یک پارامتر `<timestamp>` با فرمت زمانی `yyyy/MM/dd:HH:mm:ss` دارد که نشان میدهد آن دستور در چه زمانی اجرا شده است. از این زمان باید در دستور `log` برای فیلتر کردن لاگها استفاده کنید.
3. دستورات نامعتبر:
+ همه دستورات با حروف کوچک وارد میشوند.
+ اگر دستور دادهشده فرمت اشتباهی داشته باشد یا آرگومانهای کافی نداشته باشد، سیستم باید به صورت داخلی آن را نادیده بگیرد.
3. ورودی تضمینشده:
+ ورودیها به ترتیب زمانی داده میشوند، بنابراین نیازی به بررسی ترتیب زمانها نیست.
+ هیچ دو اتفاقی به طور همزمان رخ نمیدهد.
4. برای گرفتن امتیاز کامل، به کوچکی یا بزرگی حروف در خروجی دقت کنید.
5. در صورتی که هر یک از بخشهای مسئله را به درستی پیادهسازی کردید، امتیاز آن را مستقل از سایر بخشها خواهید گرفت. (البته، دقت کنید برای دریافت امتیاز عملیات `withdraw`، ابتدا باید عملیات `deposit` به درستی پیادهسازی شود.)
## ورودی
سطر اول ورودی شامل یک عدد صحیح و مثبت $n$ است که تعداد سطرهای ورودی را نشان میدهد.
$$1≤n≤1000$$
در $n$ سطر بعدی، هر سطر شامل یکی از دستورهای بالا است.
## خروجی
خروجیهای خواسته شده برای هر دستور را به ترتیب چاپ کنید.
## مثالها
### ورودی نمونه ۱
```
10
register user1 pass1 user 2025/01/08:09:00:00
register user2 pass2 user 2025/01/08:09:01:00
login user1 pass1 2025/01/08:09:02:00
deposit 1 300 2025/01/08:09:03:00
withdraw 1 500 2025/01/08:09:04:00
transfer 1 user3 100 2025/01/08:09:05:00
logout 1 2025/01/08:09:06:00
login user1 wrongpass 2025/01/08:09:07:00
login user2 pass2 2025/01/08:09:08:00
log error 2025/01/08:09:00:00 2025/01/08:09:10:00 2025/01/08:09:09:00
```
### خروجی نمونه ۱
```
[INFO] user registered successfully
[INFO] user registered successfully
[INFO] user logged in successfully
[INFO] amount deposited successfully
[ERROR] insufficient funds
[ERROR] target user not found
[INFO] user logged out
[ERROR] invalid credentials
[INFO] user logged in successfully
2025/01/08:09:04:00 ERROR insufficient funds
2025/01/08:09:05:00 ERROR target user not found
2025/01/08:09:07:00 ERROR invalid credentials
[DEBUG] get log error 2025/01/08:09:00:00 2025/01/08:09:10:00
```
### ورودی نمونه ۲
```
10
register user1 password1 user 2025/01/08:10:00:00
register user2 password2 user 2025/01/08:10:01:00
register admin1 adminpass admin 2025/01/08:10:02:00
login user1 password1 2025/01/08:10:03:00
deposit 1 500 2025/01/08:10:04:00
withdraw 1 100 2025/01/08:10:05:00
transfer 1 user2 200 2025/01/08:10:06:00
logout 1 2025/01/08:10:07:00
login user2 password2 2025/01/08:10:08:00
log info 2025/01/08:10:00:00 2025/01/08:10:10:00 2025/01/08:10:09:00
```
### خروجی نمونه ۲
```
[INFO] user registered successfully
[INFO] user registered successfully
[INFO] user registered successfully
[INFO] user logged in successfully
[INFO] amount deposited successfully
[INFO] amount withdrawn successfully
[INFO] amount transferred successfully
[INFO] user logged out
[INFO] user logged in successfully
2025/01/08:10:00:00 INFO user registered successfully
2025/01/08:10:01:00 INFO user registered successfully
2025/01/08:10:02:00 INFO user registered successfully
2025/01/08:10:03:00 INFO user logged in successfully
2025/01/08:10:04:00 INFO amount deposited successfully
2025/01/08:10:05:00 INFO amount withdrawn successfully
2025/01/08:10:06:00 INFO amount transferred successfully
2025/01/08:10:07:00 INFO user logged out
2025/01/08:10:08:00 INFO user logged in successfully
[DEBUG] get log info 2025/01/08:10:00:00 2025/01/08:10:10:00
```